]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_rest_s3.cc
import quincy beta 17.1.0
[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 ft=cpp
3
4 #include <errno.h>
5 #include <array>
6 #include <string.h>
7 #include <string_view>
8
9 #include "common/ceph_crypto.h"
10 #include "common/split.h"
11 #include "common/Formatter.h"
12 #include "common/utf8.h"
13 #include "common/ceph_json.h"
14 #include "common/safe_io.h"
15 #include "common/errno.h"
16 #include "auth/Crypto.h"
17 #include <boost/algorithm/string.hpp>
18 #include <boost/algorithm/string/replace.hpp>
19 #include <boost/tokenizer.hpp>
20 #define BOOST_BIND_GLOBAL_PLACEHOLDERS
21 #ifdef HAVE_WARN_IMPLICIT_CONST_INT_FLOAT_CONVERSION
22 #pragma clang diagnostic push
23 #pragma clang diagnostic ignored "-Wimplicit-const-int-float-conversion"
24 #endif
25 #ifdef HAVE_WARN_IMPLICIT_CONST_INT_FLOAT_CONVERSION
26 #pragma clang diagnostic pop
27 #endif
28 #undef BOOST_BIND_GLOBAL_PLACEHOLDERS
29
30 #include <liboath/oath.h>
31
32 #include "rgw_rest.h"
33 #include "rgw_rest_s3.h"
34 #include "rgw_rest_s3website.h"
35 #include "rgw_rest_pubsub.h"
36 #include "rgw_auth_s3.h"
37 #include "rgw_acl.h"
38 #include "rgw_policy_s3.h"
39 #include "rgw_user.h"
40 #include "rgw_cors.h"
41 #include "rgw_cors_s3.h"
42 #include "rgw_tag_s3.h"
43
44 #include "rgw_client_io.h"
45
46 #include "rgw_keystone.h"
47 #include "rgw_auth_keystone.h"
48 #include "rgw_auth_registry.h"
49
50 #include "rgw_es_query.h"
51
52 #include <typeinfo> // for 'typeid'
53
54 #include "rgw_ldap.h"
55 #include "rgw_token.h"
56 #include "rgw_rest_role.h"
57 #include "rgw_crypt.h"
58 #include "rgw_crypt_sanitize.h"
59 #include "rgw_rest_user_policy.h"
60 #include "rgw_zone.h"
61 #include "rgw_bucket_sync.h"
62
63 #include "services/svc_zone.h"
64 #include "services/svc_cls.h"
65
66 #include "include/ceph_assert.h"
67 #include "rgw_role.h"
68 #include "rgw_rest_sts.h"
69 #include "rgw_rest_iam.h"
70 #include "rgw_sts.h"
71 #include "rgw_sal_rados.h"
72
73 #include "rgw_s3select.h"
74
75 #define dout_context g_ceph_context
76 #define dout_subsys ceph_subsys_rgw
77
78 using namespace std;
79 using namespace rgw;
80 using namespace ceph::crypto;
81
82 void list_all_buckets_start(struct req_state *s)
83 {
84 s->formatter->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3);
85 }
86
87 void list_all_buckets_end(struct req_state *s)
88 {
89 s->formatter->close_section();
90 }
91
92 void dump_bucket(struct req_state *s, rgw::sal::Bucket& obj)
93 {
94 s->formatter->open_object_section("Bucket");
95 s->formatter->dump_string("Name", obj.get_name());
96 dump_time(s, "CreationDate", obj.get_creation_time());
97 s->formatter->close_section();
98 }
99
100 void rgw_get_errno_s3(rgw_http_error *e , int err_no)
101 {
102 rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no);
103
104 if (r != rgw_http_s3_errors.end()) {
105 e->http_ret = r->second.first;
106 e->s3_code = r->second.second;
107 } else {
108 e->http_ret = 500;
109 e->s3_code = "UnknownError";
110 }
111 }
112
113 static inline std::string get_s3_expiration_header(
114 struct req_state* s,
115 const ceph::real_time& mtime)
116 {
117 return rgw::lc::s3_expiration_header(
118 s, s->object->get_key(), s->tagset, mtime, s->bucket_attrs);
119 }
120
121 static inline bool get_s3_multipart_abort_header(
122 struct req_state* s, const ceph::real_time& mtime,
123 ceph::real_time& date, std::string& rule_id)
124 {
125 return rgw::lc::s3_multipart_abort_header(
126 s, s->object->get_key(), mtime, s->bucket_attrs, date, rule_id);
127 }
128
129 struct response_attr_param {
130 const char *param;
131 const char *http_attr;
132 };
133
134 static struct response_attr_param resp_attr_params[] = {
135 {"response-content-type", "Content-Type"},
136 {"response-content-language", "Content-Language"},
137 {"response-expires", "Expires"},
138 {"response-cache-control", "Cache-Control"},
139 {"response-content-disposition", "Content-Disposition"},
140 {"response-content-encoding", "Content-Encoding"},
141 {NULL, NULL},
142 };
143
144 int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) {
145 map<string, bufferlist>::iterator iter;
146 iter = attrs.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION);
147 if (iter != attrs.end()) {
148 bufferlist &bl = iter->second;
149 s->redirect = bl.c_str();
150 s->err.http_ret = 301;
151 ldpp_dout(this, 20) << __CEPH_ASSERT_FUNCTION << " redirecting per x-amz-website-redirect-location=" << s->redirect << dendl;
152 op_ret = -ERR_WEBSITE_REDIRECT;
153 set_req_state_err(s, op_ret);
154 dump_errno(s);
155 dump_content_length(s, 0);
156 dump_redirect(s, s->redirect);
157 end_header(s, this);
158 return op_ret;
159 } else {
160 return RGWGetObj_ObjStore_S3::send_response_data(bl, bl_ofs, bl_len);
161 }
162 }
163
164 int RGWGetObj_ObjStore_S3Website::send_response_data_error(optional_yield y)
165 {
166 return RGWGetObj_ObjStore_S3::send_response_data_error(y);
167 }
168
169 int RGWGetObj_ObjStore_S3::get_params(optional_yield y)
170 {
171 // for multisite sync requests, only read the slo manifest itself, rather than
172 // all of the data from its parts. the parts will sync as separate objects
173 skip_manifest = s->info.args.exists(RGW_SYS_PARAM_PREFIX "sync-manifest");
174
175 // multisite sync requests should fetch encrypted data, along with the
176 // attributes needed to support decryption on the other zone
177 if (s->system_request) {
178 skip_decrypt = s->info.args.exists(RGW_SYS_PARAM_PREFIX "skip-decrypt");
179 }
180
181 return RGWGetObj_ObjStore::get_params(y);
182 }
183
184 int RGWGetObj_ObjStore_S3::send_response_data_error(optional_yield y)
185 {
186 bufferlist bl;
187 return send_response_data(bl, 0 , 0);
188 }
189
190 template <class T>
191 int decode_attr_bl_single_value(map<string, bufferlist>& attrs, const char *attr_name, T *result, T def_val)
192 {
193 map<string, bufferlist>::iterator iter = attrs.find(attr_name);
194 if (iter == attrs.end()) {
195 *result = def_val;
196 return 0;
197 }
198 bufferlist& bl = iter->second;
199 if (bl.length() == 0) {
200 *result = def_val;
201 return 0;
202 }
203 auto bliter = bl.cbegin();
204 try {
205 decode(*result, bliter);
206 } catch (buffer::error& err) {
207 return -EIO;
208 }
209 return 0;
210 }
211
212 inline bool str_has_cntrl(const std::string s) {
213 return std::any_of(s.begin(), s.end(), ::iscntrl);
214 }
215
216 inline bool str_has_cntrl(const char* s) {
217 std::string _s(s);
218 return str_has_cntrl(_s);
219 }
220
221 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
222 off_t bl_len)
223 {
224 const char *content_type = NULL;
225 string content_type_str;
226 map<string, string> response_attrs;
227 map<string, string>::iterator riter;
228 bufferlist metadata_bl;
229
230 string expires = get_s3_expiration_header(s, lastmod);
231
232 if (sent_header)
233 goto send_data;
234
235 if (custom_http_ret) {
236 set_req_state_err(s, 0);
237 dump_errno(s, custom_http_ret);
238 } else {
239 set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
240 : op_ret);
241 dump_errno(s);
242 }
243
244 if (op_ret)
245 goto done;
246
247 if (range_str)
248 dump_range(s, start, end, s->obj_size);
249
250 if (s->system_request &&
251 s->info.args.exists(RGW_SYS_PARAM_PREFIX "prepend-metadata")) {
252
253 dump_header(s, "Rgwx-Object-Size", (long long)total_len);
254
255 if (rgwx_stat) {
256 /*
257 * in this case, we're not returning the object's content, only the prepended
258 * extra metadata
259 */
260 total_len = 0;
261 }
262
263 /* JSON encode object metadata */
264 JSONFormatter jf;
265 jf.open_object_section("obj_metadata");
266 encode_json("attrs", attrs, &jf);
267 utime_t ut(lastmod);
268 encode_json("mtime", ut, &jf);
269 jf.close_section();
270 stringstream ss;
271 jf.flush(ss);
272 metadata_bl.append(ss.str());
273 dump_header(s, "Rgwx-Embedded-Metadata-Len", metadata_bl.length());
274 total_len += metadata_bl.length();
275 }
276
277 if (s->system_request && !real_clock::is_zero(lastmod)) {
278 /* we end up dumping mtime in two different methods, a bit redundant */
279 dump_epoch_header(s, "Rgwx-Mtime", lastmod);
280 uint64_t pg_ver = 0;
281 int r = decode_attr_bl_single_value(attrs, RGW_ATTR_PG_VER, &pg_ver, (uint64_t)0);
282 if (r < 0) {
283 ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
284 }
285 dump_header(s, "Rgwx-Obj-PG-Ver", pg_ver);
286
287 uint32_t source_zone_short_id = 0;
288 r = decode_attr_bl_single_value(attrs, RGW_ATTR_SOURCE_ZONE, &source_zone_short_id, (uint32_t)0);
289 if (r < 0) {
290 ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
291 }
292 if (source_zone_short_id != 0) {
293 dump_header(s, "Rgwx-Source-Zone-Short-Id", source_zone_short_id);
294 }
295 }
296
297 for (auto &it : crypt_http_responses)
298 dump_header(s, it.first, it.second);
299
300 dump_content_length(s, total_len);
301 dump_last_modified(s, lastmod);
302 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
303 dump_header_if_nonempty(s, "x-amz-expiration", expires);
304
305 if (attrs.find(RGW_ATTR_APPEND_PART_NUM) != attrs.end()) {
306 dump_header(s, "x-rgw-object-type", "Appendable");
307 dump_header(s, "x-rgw-next-append-position", s->obj_size);
308 } else {
309 dump_header(s, "x-rgw-object-type", "Normal");
310 }
311
312 if (! op_ret) {
313 if (! lo_etag.empty()) {
314 /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
315 * legit to perform GET on them through S3 API. In such situation,
316 * a client should receive the composited content with corresponding
317 * etag value. */
318 dump_etag(s, lo_etag);
319 } else {
320 auto iter = attrs.find(RGW_ATTR_ETAG);
321 if (iter != attrs.end()) {
322 dump_etag(s, iter->second.to_str());
323 }
324 }
325
326 for (struct response_attr_param *p = resp_attr_params; p->param; p++) {
327 bool exists;
328 string val = s->info.args.get(p->param, &exists);
329 if (exists) {
330 /* reject unauthenticated response header manipulation, see
331 * https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html */
332 if (s->auth.identity->is_anonymous()) {
333 return -ERR_INVALID_REQUEST;
334 }
335 /* HTTP specification says no control characters should be present in
336 * header values: https://tools.ietf.org/html/rfc7230#section-3.2
337 * field-vchar = VCHAR / obs-text
338 *
339 * Failure to validate this permits a CRLF injection in HTTP headers,
340 * whereas S3 GetObject only permits specific headers.
341 */
342 if(str_has_cntrl(val)) {
343 /* TODO: return a more distinct error in future;
344 * stating what the problem is */
345 return -ERR_INVALID_REQUEST;
346 }
347
348 if (strcmp(p->param, "response-content-type") != 0) {
349 response_attrs[p->http_attr] = val;
350 } else {
351 content_type_str = val;
352 content_type = content_type_str.c_str();
353 }
354 }
355 }
356
357 for (auto iter = attrs.begin(); iter != attrs.end(); ++iter) {
358 const char *name = iter->first.c_str();
359 map<string, string>::iterator aiter = rgw_to_http_attrs.find(name);
360 if (aiter != rgw_to_http_attrs.end()) {
361 if (response_attrs.count(aiter->second) == 0) {
362 /* Was not already overridden by a response param. */
363
364 size_t len = iter->second.length();
365 string s(iter->second.c_str(), len);
366 while (len && !s[len - 1]) {
367 --len;
368 s.resize(len);
369 }
370 response_attrs[aiter->second] = s;
371 }
372 } else if (iter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) {
373 /* Special handling for content_type. */
374 if (!content_type) {
375 content_type_str = rgw_bl_str(iter->second);
376 content_type = content_type_str.c_str();
377 }
378 } else if (strcmp(name, RGW_ATTR_SLO_UINDICATOR) == 0) {
379 // this attr has an extra length prefix from encode() in prior versions
380 dump_header(s, "X-Object-Meta-Static-Large-Object", "True");
381 } else if (strncmp(name, RGW_ATTR_META_PREFIX,
382 sizeof(RGW_ATTR_META_PREFIX)-1) == 0) {
383 /* User custom metadata. */
384 name += sizeof(RGW_ATTR_PREFIX) - 1;
385 dump_header(s, name, iter->second);
386 } else if (iter->first.compare(RGW_ATTR_TAGS) == 0) {
387 RGWObjTags obj_tags;
388 try{
389 auto it = iter->second.cbegin();
390 obj_tags.decode(it);
391 } catch (buffer::error &err) {
392 ldpp_dout(this,0) << "Error caught buffer::error couldn't decode TagSet " << dendl;
393 }
394 dump_header(s, RGW_AMZ_TAG_COUNT, obj_tags.count());
395 } else if (iter->first.compare(RGW_ATTR_OBJECT_RETENTION) == 0 && get_retention){
396 RGWObjectRetention retention;
397 try {
398 decode(retention, iter->second);
399 dump_header(s, "x-amz-object-lock-mode", retention.get_mode());
400 string date = ceph::to_iso_8601(retention.get_retain_until_date());
401 dump_header(s, "x-amz-object-lock-retain-until-date", date.c_str());
402 } catch (buffer::error& err) {
403 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
404 }
405 } else if (iter->first.compare(RGW_ATTR_OBJECT_LEGAL_HOLD) == 0 && get_legal_hold) {
406 RGWObjectLegalHold legal_hold;
407 try {
408 decode(legal_hold, iter->second);
409 dump_header(s, "x-amz-object-lock-legal-hold",legal_hold.get_status());
410 } catch (buffer::error& err) {
411 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl;
412 }
413 }
414 }
415 }
416
417 done:
418 for (riter = response_attrs.begin(); riter != response_attrs.end();
419 ++riter) {
420 dump_header(s, riter->first, riter->second);
421 }
422
423 if (op_ret == -ERR_NOT_MODIFIED) {
424 end_header(s, this);
425 } else {
426 if (!content_type)
427 content_type = "binary/octet-stream";
428
429 end_header(s, this, content_type);
430 }
431
432 if (metadata_bl.length()) {
433 dump_body(s, metadata_bl);
434 }
435 sent_header = true;
436
437 send_data:
438 if (get_data && !op_ret) {
439 int r = dump_body(s, bl.c_str() + bl_ofs, bl_len);
440 if (r < 0)
441 return r;
442 }
443
444 return 0;
445 }
446
447 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetObj_Filter> *filter, RGWGetObj_Filter* cb, bufferlist* manifest_bl)
448 {
449 if (skip_decrypt) { // bypass decryption for multisite sync requests
450 return 0;
451 }
452
453 int res = 0;
454 std::unique_ptr<BlockCrypt> block_crypt;
455 res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses);
456 if (res == 0) {
457 if (block_crypt != nullptr) {
458 auto f = std::make_unique<RGWGetObj_BlockDecrypt>(s, s->cct, cb, std::move(block_crypt));
459 if (manifest_bl != nullptr) {
460 res = f->read_manifest(this, *manifest_bl);
461 if (res == 0) {
462 *filter = std::move(f);
463 }
464 }
465 }
466 }
467 return res;
468 }
469 int RGWGetObj_ObjStore_S3::verify_requester(const rgw::auth::StrategyRegistry& auth_registry, optional_yield y)
470 {
471 int ret = -EINVAL;
472 ret = RGWOp::verify_requester(auth_registry, y);
473 if(!s->user->get_caps().check_cap("amz-cache", RGW_CAP_READ) && !ret && s->info.env->exists("HTTP_X_AMZ_CACHE"))
474 ret = override_range_hdr(auth_registry, y);
475 return ret;
476 }
477
478 int RGWGetObj_ObjStore_S3::override_range_hdr(const rgw::auth::StrategyRegistry& auth_registry, optional_yield y)
479 {
480 int ret = -EINVAL;
481 ldpp_dout(this, 10) << "cache override headers" << dendl;
482 RGWEnv* rgw_env = const_cast<RGWEnv *>(s->info.env);
483 const char* backup_range = rgw_env->get("HTTP_RANGE");
484 const char hdrs_split[2] = {(char)178,'\0'};
485 const char kv_split[2] = {(char)177,'\0'};
486 const char* cache_hdr = rgw_env->get("HTTP_X_AMZ_CACHE");
487 for (std::string_view hdr : ceph::split(cache_hdr, hdrs_split)) {
488 auto kv = ceph::split(hdr, kv_split);
489 auto k = kv.begin();
490 if (std::distance(k, kv.end()) != 2) {
491 return -EINVAL;
492 }
493 auto v = std::next(k);
494 std::string key = "HTTP_";
495 key.append(*k);
496 boost::replace_all(key, "-", "_");
497 rgw_env->set(std::move(key), std::string(*v));
498 ldpp_dout(this, 10) << "after splitting cache kv key: " << key << " " << rgw_env->get(key.c_str()) << dendl;
499 }
500 ret = RGWOp::verify_requester(auth_registry, y);
501 if(!ret && backup_range) {
502 rgw_env->set("HTTP_RANGE",backup_range);
503 } else {
504 rgw_env->remove("HTTP_RANGE");
505 }
506 return ret;
507 }
508
509
510 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist& bl)
511 {
512 dump_errno(s);
513 end_header(s, this, "application/xml");
514 dump_start(s);
515
516 s->formatter->open_object_section_in_ns("Tagging", XMLNS_AWS_S3);
517 s->formatter->open_object_section("TagSet");
518 if (has_tags){
519 RGWObjTagSet_S3 tagset;
520 auto iter = bl.cbegin();
521 try {
522 tagset.decode(iter);
523 } catch (buffer::error& err) {
524 ldpp_dout(this,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
525 op_ret= -EIO;
526 return;
527 }
528 tagset.dump_xml(s->formatter);
529 }
530 s->formatter->close_section();
531 s->formatter->close_section();
532 rgw_flush_formatter_and_reset(s, s->formatter);
533 }
534
535
536 int RGWPutObjTags_ObjStore_S3::get_params(optional_yield y)
537 {
538 RGWXMLParser parser;
539
540 if (!parser.init()){
541 return -EINVAL;
542 }
543
544 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
545
546 int r = 0;
547 bufferlist data;
548 std::tie(r, data) = read_all_input(s, max_size, false);
549
550 if (r < 0)
551 return r;
552
553 if (!parser.parse(data.c_str(), data.length(), 1)) {
554 return -ERR_MALFORMED_XML;
555 }
556
557 RGWObjTagging_S3 tagging;
558
559 try {
560 RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
561 } catch (RGWXMLDecoder::err& err) {
562 ldpp_dout(this, 5) << "Malformed tagging request: " << err << dendl;
563 return -ERR_MALFORMED_XML;
564 }
565
566 RGWObjTags obj_tags;
567 r = tagging.rebuild(obj_tags);
568 if (r < 0)
569 return r;
570
571 obj_tags.encode(tags_bl);
572 ldpp_dout(this, 20) << "Read " << obj_tags.count() << "tags" << dendl;
573
574 return 0;
575 }
576
577 void RGWPutObjTags_ObjStore_S3::send_response()
578 {
579 if (op_ret)
580 set_req_state_err(s, op_ret);
581 dump_errno(s);
582 end_header(s, this, "application/xml");
583 dump_start(s);
584
585 }
586
587 void RGWDeleteObjTags_ObjStore_S3::send_response()
588 {
589 int r = op_ret;
590 if (r == -ENOENT)
591 r = 0;
592 if (!r)
593 r = STATUS_NO_CONTENT;
594
595 set_req_state_err(s, r);
596 dump_errno(s);
597 end_header(s, this);
598 }
599
600 void RGWGetBucketTags_ObjStore_S3::send_response_data(bufferlist& bl)
601 {
602 if (op_ret)
603 set_req_state_err(s, op_ret);
604 dump_errno(s);
605 end_header(s, this, "application/xml");
606 dump_start(s);
607
608 if (!op_ret) {
609 s->formatter->open_object_section_in_ns("Tagging", XMLNS_AWS_S3);
610 s->formatter->open_object_section("TagSet");
611 if (has_tags){
612 RGWObjTagSet_S3 tagset;
613 auto iter = bl.cbegin();
614 try {
615 tagset.decode(iter);
616 } catch (buffer::error& err) {
617 ldpp_dout(this,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
618 op_ret= -EIO;
619 return;
620 }
621 tagset.dump_xml(s->formatter);
622 }
623 s->formatter->close_section();
624 s->formatter->close_section();
625 rgw_flush_formatter_and_reset(s, s->formatter);
626 }
627 }
628
629 int RGWPutBucketTags_ObjStore_S3::get_params(const DoutPrefixProvider *dpp, optional_yield y)
630 {
631 RGWXMLParser parser;
632
633 if (!parser.init()){
634 return -EINVAL;
635 }
636
637 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
638 int r = 0;
639 bufferlist data;
640
641 std::tie(r, data) = read_all_input(s, max_size, false);
642
643 if (r < 0)
644 return r;
645
646 if (!parser.parse(data.c_str(), data.length(), 1)) {
647 return -ERR_MALFORMED_XML;
648 }
649
650 RGWObjTagging_S3 tagging;
651 try {
652 RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
653 } catch (RGWXMLDecoder::err& err) {
654
655 ldpp_dout(dpp, 5) << "Malformed tagging request: " << err << dendl;
656 return -ERR_MALFORMED_XML;
657 }
658
659 RGWObjTags obj_tags(50); // A tag set can contain as many as 50 tags, or it can be empty.
660 r = tagging.rebuild(obj_tags);
661 if (r < 0)
662 return r;
663
664 obj_tags.encode(tags_bl);
665 ldpp_dout(dpp, 20) << "Read " << obj_tags.count() << "tags" << dendl;
666
667 // forward bucket tags requests to meta master zone
668 if (!store->is_meta_master()) {
669 /* only need to keep this data around if we're not meta master */
670 in_data = std::move(data);
671 }
672
673 return 0;
674 }
675
676 void RGWPutBucketTags_ObjStore_S3::send_response()
677 {
678 if (op_ret)
679 set_req_state_err(s, op_ret);
680 dump_errno(s);
681 end_header(s, this, "application/xml");
682 dump_start(s);
683 }
684
685 void RGWDeleteBucketTags_ObjStore_S3::send_response()
686 {
687 if (op_ret)
688 set_req_state_err(s, op_ret);
689 dump_errno(s);
690 end_header(s, this, "application/xml");
691 dump_start(s);
692 }
693
694 namespace {
695
696 bool is_valid_status(const string& s) {
697 return (s == "Enabled" ||
698 s == "Disabled");
699 }
700
701 static string enabled_group_id = "s3-bucket-replication:enabled";
702 static string disabled_group_id = "s3-bucket-replication:disabled";
703
704 struct ReplicationConfiguration {
705 string role;
706
707 struct Rule {
708 struct DeleteMarkerReplication {
709 string status;
710
711 void decode_xml(XMLObj *obj) {
712 RGWXMLDecoder::decode_xml("Status", status, obj);
713 }
714
715 void dump_xml(Formatter *f) const {
716 encode_xml("Status", status, f);
717 }
718
719 bool is_valid(CephContext *cct) const {
720 bool result = is_valid_status(status);
721 if (!result) {
722 ldout(cct, 5) << "NOTICE: bad status provided in DeleteMarkerReplication element (status=" << status << ")" << dendl;
723 }
724 return result;
725 }
726 };
727
728 struct Source { /* rgw extension */
729 std::vector<string> zone_names;
730
731 void decode_xml(XMLObj *obj) {
732 RGWXMLDecoder::decode_xml("Zone", zone_names, obj);
733 }
734
735 void dump_xml(Formatter *f) const {
736 encode_xml("Zone", zone_names, f);
737 }
738 };
739
740 struct Destination {
741 struct AccessControlTranslation {
742 string owner;
743
744 void decode_xml(XMLObj *obj) {
745 RGWXMLDecoder::decode_xml("Owner", owner, obj);
746 }
747 void dump_xml(Formatter *f) const {
748 encode_xml("Owner", owner, f);
749 }
750 };
751
752 std::optional<AccessControlTranslation> acl_translation;
753 std::optional<string> account;
754 string bucket;
755 std::optional<string> storage_class;
756 std::vector<string> zone_names;
757
758 void decode_xml(XMLObj *obj) {
759 RGWXMLDecoder::decode_xml("AccessControlTranslation", acl_translation, obj);
760 RGWXMLDecoder::decode_xml("Account", account, obj);
761 if (account && account->empty()) {
762 account.reset();
763 }
764 RGWXMLDecoder::decode_xml("Bucket", bucket, obj);
765 RGWXMLDecoder::decode_xml("StorageClass", storage_class, obj);
766 if (storage_class && storage_class->empty()) {
767 storage_class.reset();
768 }
769 RGWXMLDecoder::decode_xml("Zone", zone_names, obj); /* rgw extension */
770 }
771
772 void dump_xml(Formatter *f) const {
773 encode_xml("AccessControlTranslation", acl_translation, f);
774 encode_xml("Account", account, f);
775 encode_xml("Bucket", bucket, f);
776 encode_xml("StorageClass", storage_class, f);
777 encode_xml("Zone", zone_names, f);
778 }
779 };
780
781 struct Filter {
782 struct Tag {
783 string key;
784 string value;
785
786 bool empty() const {
787 return key.empty() && value.empty();
788 }
789
790 void decode_xml(XMLObj *obj) {
791 RGWXMLDecoder::decode_xml("Key", key, obj);
792 RGWXMLDecoder::decode_xml("Value", value, obj);
793 };
794
795 void dump_xml(Formatter *f) const {
796 encode_xml("Key", key, f);
797 encode_xml("Value", value, f);
798 }
799 };
800
801 struct AndElements {
802 std::optional<string> prefix;
803 std::vector<Tag> tags;
804
805 bool empty() const {
806 return !prefix &&
807 (tags.size() == 0);
808 }
809
810 void decode_xml(XMLObj *obj) {
811 std::vector<Tag> _tags;
812 RGWXMLDecoder::decode_xml("Prefix", prefix, obj);
813 if (prefix && prefix->empty()) {
814 prefix.reset();
815 }
816 RGWXMLDecoder::decode_xml("Tag", _tags, obj);
817 for (auto& t : _tags) {
818 if (!t.empty()) {
819 tags.push_back(std::move(t));
820 }
821 }
822 };
823
824 void dump_xml(Formatter *f) const {
825 encode_xml("Prefix", prefix, f);
826 encode_xml("Tag", tags, f);
827 }
828 };
829
830 std::optional<string> prefix;
831 std::optional<Tag> tag;
832 std::optional<AndElements> and_elements;
833
834 bool empty() const {
835 return (!prefix && !tag && !and_elements);
836 }
837
838 void decode_xml(XMLObj *obj) {
839 RGWXMLDecoder::decode_xml("Prefix", prefix, obj);
840 if (prefix && prefix->empty()) {
841 prefix.reset();
842 }
843 RGWXMLDecoder::decode_xml("Tag", tag, obj);
844 if (tag && tag->empty()) {
845 tag.reset();
846 }
847 RGWXMLDecoder::decode_xml("And", and_elements, obj);
848 if (and_elements && and_elements->empty()) {
849 and_elements.reset();
850 }
851 };
852
853 void dump_xml(Formatter *f) const {
854 encode_xml("Prefix", prefix, f);
855 encode_xml("Tag", tag, f);
856 encode_xml("And", and_elements, f);
857 }
858
859 bool is_valid(CephContext *cct) const {
860 if (tag && prefix) {
861 ldout(cct, 5) << "NOTICE: both tag and prefix were provided in replication filter rule" << dendl;
862 return false;
863 }
864
865 if (and_elements) {
866 if (prefix && and_elements->prefix) {
867 ldout(cct, 5) << "NOTICE: too many prefixes were provided in re" << dendl;
868 return false;
869 }
870 }
871 return true;
872 };
873
874 int to_sync_pipe_filter(CephContext *cct,
875 rgw_sync_pipe_filter *f) const {
876 if (!is_valid(cct)) {
877 return -EINVAL;
878 }
879 if (prefix) {
880 f->prefix = *prefix;
881 }
882 if (tag) {
883 f->tags.insert(rgw_sync_pipe_filter_tag(tag->key, tag->value));
884 }
885
886 if (and_elements) {
887 if (and_elements->prefix) {
888 f->prefix = *and_elements->prefix;
889 }
890 for (auto& t : and_elements->tags) {
891 f->tags.insert(rgw_sync_pipe_filter_tag(t.key, t.value));
892 }
893 }
894 return 0;
895 }
896
897 void from_sync_pipe_filter(const rgw_sync_pipe_filter& f) {
898 if (f.prefix && f.tags.empty()) {
899 prefix = f.prefix;
900 return;
901 }
902 if (f.prefix) {
903 and_elements.emplace();
904 and_elements->prefix = f.prefix;
905 } else if (f.tags.size() == 1) {
906 auto iter = f.tags.begin();
907 if (iter == f.tags.end()) {
908 /* should never happen */
909 return;
910 }
911 auto& t = *iter;
912 tag.emplace();
913 tag->key = t.key;
914 tag->value = t.value;
915 return;
916 }
917
918 if (f.tags.empty()) {
919 return;
920 }
921
922 if (!and_elements) {
923 and_elements.emplace();
924 }
925
926 for (auto& t : f.tags) {
927 auto& tag = and_elements->tags.emplace_back();
928 tag.key = t.key;
929 tag.value = t.value;
930 }
931 }
932 };
933
934 set<rgw_zone_id> get_zone_ids_from_names(rgw::sal::Store* store,
935 const vector<string>& zone_names) const {
936 set<rgw_zone_id> ids;
937
938 for (auto& name : zone_names) {
939 rgw_zone_id id;
940 if (static_cast<rgw::sal::RadosStore*>(store)->svc()->zone->find_zone_id_by_name(name, &id)) {
941 ids.insert(std::move(id));
942 }
943 }
944
945 return ids;
946 }
947
948 vector<string> get_zone_names_from_ids(rgw::sal::Store* store,
949 const set<rgw_zone_id>& zone_ids) const {
950 vector<string> names;
951
952 for (auto& id : zone_ids) {
953 RGWZone *zone;
954 if (static_cast<rgw::sal::RadosStore*>(store)->svc()->zone->find_zone(id, &zone)) {
955 names.emplace_back(zone->name);
956 }
957 }
958
959 return names;
960 }
961
962 std::optional<DeleteMarkerReplication> delete_marker_replication;
963 std::optional<Source> source;
964 Destination destination;
965 std::optional<Filter> filter;
966 string id;
967 int32_t priority;
968 string status;
969
970 void decode_xml(XMLObj *obj) {
971 RGWXMLDecoder::decode_xml("DeleteMarkerReplication", delete_marker_replication, obj);
972 RGWXMLDecoder::decode_xml("Source", source, obj);
973 RGWXMLDecoder::decode_xml("Destination", destination, obj);
974 RGWXMLDecoder::decode_xml("ID", id, obj);
975
976 std::optional<string> prefix;
977 RGWXMLDecoder::decode_xml("Prefix", prefix, obj);
978 if (prefix) {
979 filter.emplace();
980 filter->prefix = prefix;
981 }
982
983 if (!filter) {
984 RGWXMLDecoder::decode_xml("Filter", filter, obj);
985 } else {
986 /* don't want to have filter reset because it might have been initialized
987 * when decoding prefix
988 */
989 RGWXMLDecoder::decode_xml("Filter", *filter, obj);
990 }
991
992 RGWXMLDecoder::decode_xml("Priority", priority, obj);
993 RGWXMLDecoder::decode_xml("Status", status, obj);
994 }
995
996 void dump_xml(Formatter *f) const {
997 encode_xml("DeleteMarkerReplication", delete_marker_replication, f);
998 encode_xml("Source", source, f);
999 encode_xml("Destination", destination, f);
1000 encode_xml("Filter", filter, f);
1001 encode_xml("ID", id, f);
1002 encode_xml("Priority", priority, f);
1003 encode_xml("Status", status, f);
1004 }
1005
1006 bool is_valid(CephContext *cct) const {
1007 if (!is_valid_status(status)) {
1008 ldout(cct, 5) << "NOTICE: bad status provided in rule (status=" << status << ")" << dendl;
1009 return false;
1010 }
1011 if ((filter && !filter->is_valid(cct)) ||
1012 (delete_marker_replication && !delete_marker_replication->is_valid(cct))) {
1013 return false;
1014 }
1015 return true;
1016 }
1017
1018 int to_sync_policy_pipe(req_state *s, rgw::sal::Store* store,
1019 rgw_sync_bucket_pipes *pipe,
1020 bool *enabled) const {
1021 if (!is_valid(s->cct)) {
1022 return -EINVAL;
1023 }
1024
1025 pipe->id = id;
1026 pipe->params.priority = priority;
1027
1028 const auto& user_id = s->user->get_id();
1029
1030 rgw_bucket_key dest_bk(user_id.tenant,
1031 destination.bucket);
1032
1033 if (source && !source->zone_names.empty()) {
1034 pipe->source.zones = get_zone_ids_from_names(store, source->zone_names);
1035 } else {
1036 pipe->source.set_all_zones(true);
1037 }
1038 if (!destination.zone_names.empty()) {
1039 pipe->dest.zones = get_zone_ids_from_names(store, destination.zone_names);
1040 } else {
1041 pipe->dest.set_all_zones(true);
1042 }
1043 pipe->dest.bucket.emplace(dest_bk);
1044
1045 if (filter) {
1046 int r = filter->to_sync_pipe_filter(s->cct, &pipe->params.source.filter);
1047 if (r < 0) {
1048 return r;
1049 }
1050 }
1051 if (destination.acl_translation) {
1052 rgw_user u;
1053 u.tenant = user_id.tenant;
1054 u.from_str(destination.acl_translation->owner); /* explicit tenant will override tenant,
1055 otherwise will inherit it from s->user */
1056 pipe->params.dest.acl_translation.emplace();
1057 pipe->params.dest.acl_translation->owner = u;
1058 }
1059 pipe->params.dest.storage_class = destination.storage_class;
1060
1061 *enabled = (status == "Enabled");
1062
1063 pipe->params.mode = rgw_sync_pipe_params::Mode::MODE_USER;
1064 pipe->params.user = user_id.to_str();
1065
1066 return 0;
1067 }
1068
1069 void from_sync_policy_pipe(rgw::sal::Store* store,
1070 const rgw_sync_bucket_pipes& pipe,
1071 bool enabled) {
1072 id = pipe.id;
1073 status = (enabled ? "Enabled" : "Disabled");
1074 priority = pipe.params.priority;
1075
1076 if (pipe.source.all_zones) {
1077 source.reset();
1078 } else if (pipe.source.zones) {
1079 source.emplace();
1080 source->zone_names = get_zone_names_from_ids(store, *pipe.source.zones);
1081 }
1082
1083 if (!pipe.dest.all_zones &&
1084 pipe.dest.zones) {
1085 destination.zone_names = get_zone_names_from_ids(store, *pipe.dest.zones);
1086 }
1087
1088 if (pipe.params.dest.acl_translation) {
1089 destination.acl_translation.emplace();
1090 destination.acl_translation->owner = pipe.params.dest.acl_translation->owner.to_str();
1091 }
1092
1093 if (pipe.params.dest.storage_class) {
1094 destination.storage_class = *pipe.params.dest.storage_class;
1095 }
1096
1097 if (pipe.dest.bucket) {
1098 destination.bucket = pipe.dest.bucket->get_key();
1099 }
1100
1101 filter.emplace();
1102 filter->from_sync_pipe_filter(pipe.params.source.filter);
1103
1104 if (filter->empty()) {
1105 filter.reset();
1106 }
1107 }
1108 };
1109
1110 std::vector<Rule> rules;
1111
1112 void decode_xml(XMLObj *obj) {
1113 RGWXMLDecoder::decode_xml("Role", role, obj);
1114 RGWXMLDecoder::decode_xml("Rule", rules, obj);
1115 }
1116
1117 void dump_xml(Formatter *f) const {
1118 encode_xml("Role", role, f);
1119 encode_xml("Rule", rules, f);
1120 }
1121
1122 int to_sync_policy_groups(req_state *s, rgw::sal::Store* store,
1123 vector<rgw_sync_policy_group> *result) const {
1124 result->resize(2);
1125
1126 rgw_sync_policy_group& enabled_group = (*result)[0];
1127 rgw_sync_policy_group& disabled_group = (*result)[1];
1128
1129 enabled_group.id = enabled_group_id;
1130 enabled_group.status = rgw_sync_policy_group::Status::ENABLED;
1131 disabled_group.id = disabled_group_id;
1132 disabled_group.status = rgw_sync_policy_group::Status::ALLOWED; /* not enabled, not forbidden */
1133
1134 for (auto& rule : rules) {
1135 rgw_sync_bucket_pipes pipe;
1136 bool enabled;
1137 int r = rule.to_sync_policy_pipe(s, store, &pipe, &enabled);
1138 if (r < 0) {
1139 ldpp_dout(s, 5) << "NOTICE: failed to convert replication configuration into sync policy pipe (rule.id=" << rule.id << "): " << cpp_strerror(-r) << dendl;
1140 return r;
1141 }
1142
1143 if (enabled) {
1144 enabled_group.pipes.emplace_back(std::move(pipe));
1145 } else {
1146 disabled_group.pipes.emplace_back(std::move(pipe));
1147 }
1148 }
1149 return 0;
1150 }
1151
1152 void from_sync_policy_group(rgw::sal::Store* store,
1153 const rgw_sync_policy_group& group) {
1154
1155 bool enabled = (group.status == rgw_sync_policy_group::Status::ENABLED);
1156
1157 for (auto& pipe : group.pipes) {
1158 auto& rule = rules.emplace_back();
1159 rule.from_sync_policy_pipe(store, pipe, enabled);
1160 }
1161 }
1162 };
1163
1164 }
1165
1166 void RGWGetBucketReplication_ObjStore_S3::send_response_data()
1167 {
1168 if (op_ret)
1169 set_req_state_err(s, op_ret);
1170 dump_errno(s);
1171 end_header(s, this, "application/xml");
1172 dump_start(s);
1173
1174 ReplicationConfiguration conf;
1175
1176 if (s->bucket->get_info().sync_policy) {
1177 auto policy = s->bucket->get_info().sync_policy;
1178
1179 auto iter = policy->groups.find(enabled_group_id);
1180 if (iter != policy->groups.end()) {
1181 conf.from_sync_policy_group(store, iter->second);
1182 }
1183 iter = policy->groups.find(disabled_group_id);
1184 if (iter != policy->groups.end()) {
1185 conf.from_sync_policy_group(store, iter->second);
1186 }
1187 }
1188
1189 if (!op_ret) {
1190 s->formatter->open_object_section_in_ns("ReplicationConfiguration", XMLNS_AWS_S3);
1191 conf.dump_xml(s->formatter);
1192 s->formatter->close_section();
1193 rgw_flush_formatter_and_reset(s, s->formatter);
1194 }
1195 }
1196
1197 int RGWPutBucketReplication_ObjStore_S3::get_params(optional_yield y)
1198 {
1199 RGWXMLParser parser;
1200
1201 if (!parser.init()){
1202 return -EINVAL;
1203 }
1204
1205 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
1206 int r = 0;
1207 bufferlist data;
1208
1209 std::tie(r, data) = read_all_input(s, max_size, false);
1210
1211 if (r < 0)
1212 return r;
1213
1214 if (!parser.parse(data.c_str(), data.length(), 1)) {
1215 return -ERR_MALFORMED_XML;
1216 }
1217
1218 ReplicationConfiguration conf;
1219 try {
1220 RGWXMLDecoder::decode_xml("ReplicationConfiguration", conf, &parser);
1221 } catch (RGWXMLDecoder::err& err) {
1222
1223 ldpp_dout(this, 5) << "Malformed tagging request: " << err << dendl;
1224 return -ERR_MALFORMED_XML;
1225 }
1226
1227 r = conf.to_sync_policy_groups(s, store, &sync_policy_groups);
1228 if (r < 0) {
1229 return r;
1230 }
1231
1232 // forward requests to meta master zone
1233 if (!store->is_meta_master()) {
1234 /* only need to keep this data around if we're not meta master */
1235 in_data = std::move(data);
1236 }
1237
1238 return 0;
1239 }
1240
1241 void RGWPutBucketReplication_ObjStore_S3::send_response()
1242 {
1243 if (op_ret)
1244 set_req_state_err(s, op_ret);
1245 dump_errno(s);
1246 end_header(s, this, "application/xml");
1247 dump_start(s);
1248 }
1249
1250 void RGWDeleteBucketReplication_ObjStore_S3::update_sync_policy(rgw_sync_policy_info *policy)
1251 {
1252 policy->groups.erase(enabled_group_id);
1253 policy->groups.erase(disabled_group_id);
1254 }
1255
1256 void RGWDeleteBucketReplication_ObjStore_S3::send_response()
1257 {
1258 if (op_ret)
1259 set_req_state_err(s, op_ret);
1260 dump_errno(s);
1261 end_header(s, this, "application/xml");
1262 dump_start(s);
1263 }
1264
1265 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets)
1266 {
1267 if (op_ret)
1268 set_req_state_err(s, op_ret);
1269 dump_errno(s);
1270 dump_start(s);
1271 // Explicitly use chunked transfer encoding so that we can stream the result
1272 // to the user without having to wait for the full length of it.
1273 end_header(s, NULL, "application/xml", CHUNKED_TRANSFER_ENCODING);
1274
1275 if (! op_ret) {
1276 list_all_buckets_start(s);
1277 dump_owner(s, s->user->get_id(), s->user->get_display_name());
1278 s->formatter->open_array_section("Buckets");
1279 sent_data = true;
1280 }
1281 }
1282
1283 void RGWListBuckets_ObjStore_S3::send_response_data(rgw::sal::BucketList& buckets)
1284 {
1285 if (!sent_data)
1286 return;
1287
1288 auto& m = buckets.get_buckets();
1289
1290 for (auto iter = m.begin(); iter != m.end(); ++iter) {
1291 auto& bucket = iter->second;
1292 dump_bucket(s, *bucket);
1293 }
1294 rgw_flush_formatter(s, s->formatter);
1295 }
1296
1297 void RGWListBuckets_ObjStore_S3::send_response_end()
1298 {
1299 if (sent_data) {
1300 s->formatter->close_section();
1301 list_all_buckets_end(s);
1302 rgw_flush_formatter_and_reset(s, s->formatter);
1303 }
1304 }
1305
1306 int RGWGetUsage_ObjStore_S3::get_params(optional_yield y)
1307 {
1308 start_date = s->info.args.get("start-date");
1309 end_date = s->info.args.get("end-date");
1310 return 0;
1311 }
1312
1313 static void dump_usage_categories_info(Formatter *formatter, const rgw_usage_log_entry& entry, map<string, bool> *categories)
1314 {
1315 formatter->open_array_section("categories");
1316 map<string, rgw_usage_data>::const_iterator uiter;
1317 for (uiter = entry.usage_map.begin(); uiter != entry.usage_map.end(); ++uiter) {
1318 if (categories && !categories->empty() && !categories->count(uiter->first))
1319 continue;
1320 const rgw_usage_data& usage = uiter->second;
1321 formatter->open_object_section("Entry");
1322 encode_json("Category", uiter->first, formatter);
1323 encode_json("BytesSent", usage.bytes_sent, formatter);
1324 encode_json("BytesReceived", usage.bytes_received, formatter);
1325 encode_json("Ops", usage.ops, formatter);
1326 encode_json("SuccessfulOps", usage.successful_ops, formatter);
1327 formatter->close_section(); // Entry
1328 }
1329 formatter->close_section(); // Category
1330 }
1331
1332 static void dump_usage_bucket_info(Formatter *formatter, const std::string& name, const bucket_meta_entry& entry)
1333 {
1334 formatter->open_object_section("Entry");
1335 encode_json("Bucket", name, formatter);
1336 encode_json("Bytes", entry.size, formatter);
1337 encode_json("Bytes_Rounded", entry.size_rounded, formatter);
1338 formatter->close_section(); // entry
1339 }
1340
1341 void RGWGetUsage_ObjStore_S3::send_response()
1342 {
1343 if (op_ret < 0)
1344 set_req_state_err(s, op_ret);
1345 dump_errno(s);
1346
1347 // Explicitly use chunked transfer encoding so that we can stream the result
1348 // to the user without having to wait for the full length of it.
1349 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
1350 dump_start(s);
1351 if (op_ret < 0)
1352 return;
1353
1354 Formatter *formatter = s->formatter;
1355 string last_owner;
1356 bool user_section_open = false;
1357
1358 formatter->open_object_section("Usage");
1359 if (show_log_entries) {
1360 formatter->open_array_section("Entries");
1361 }
1362 map<rgw_user_bucket, rgw_usage_log_entry>::iterator iter;
1363 for (iter = usage.begin(); iter != usage.end(); ++iter) {
1364 const rgw_user_bucket& ub = iter->first;
1365 const rgw_usage_log_entry& entry = iter->second;
1366
1367 if (show_log_entries) {
1368 if (ub.user.compare(last_owner) != 0) {
1369 if (user_section_open) {
1370 formatter->close_section();
1371 formatter->close_section();
1372 }
1373 formatter->open_object_section("User");
1374 formatter->dump_string("Owner", ub.user);
1375 formatter->open_array_section("Buckets");
1376 user_section_open = true;
1377 last_owner = ub.user;
1378 }
1379 formatter->open_object_section("Bucket");
1380 formatter->dump_string("Bucket", ub.bucket);
1381 utime_t ut(entry.epoch, 0);
1382 ut.gmtime(formatter->dump_stream("Time"));
1383 formatter->dump_int("Epoch", entry.epoch);
1384 dump_usage_categories_info(formatter, entry, &categories);
1385 formatter->close_section(); // bucket
1386 }
1387
1388 summary_map[ub.user].aggregate(entry, &categories);
1389 }
1390
1391 if (show_log_entries) {
1392 if (user_section_open) {
1393 formatter->close_section(); // buckets
1394 formatter->close_section(); //user
1395 }
1396 formatter->close_section(); // entries
1397 }
1398
1399 if (show_log_sum) {
1400 formatter->open_array_section("Summary");
1401 map<string, rgw_usage_log_entry>::iterator siter;
1402 for (siter = summary_map.begin(); siter != summary_map.end(); ++siter) {
1403 const rgw_usage_log_entry& entry = siter->second;
1404 formatter->open_object_section("User");
1405 formatter->dump_string("User", siter->first);
1406 dump_usage_categories_info(formatter, entry, &categories);
1407 rgw_usage_data total_usage;
1408 entry.sum(total_usage, categories);
1409 formatter->open_object_section("Total");
1410 encode_json("BytesSent", total_usage.bytes_sent, formatter);
1411 encode_json("BytesReceived", total_usage.bytes_received, formatter);
1412 encode_json("Ops", total_usage.ops, formatter);
1413 encode_json("SuccessfulOps", total_usage.successful_ops, formatter);
1414 formatter->close_section(); // total
1415 formatter->close_section(); // user
1416 }
1417
1418 if (s->cct->_conf->rgw_rest_getusage_op_compat) {
1419 formatter->open_object_section("Stats");
1420 }
1421
1422 // send info about quota config
1423 auto user_info = s->user->get_info();
1424 encode_json("QuotaMaxBytes", user_info.user_quota.max_size, formatter);
1425 encode_json("QuotaMaxBuckets", user_info.max_buckets, formatter);
1426 encode_json("QuotaMaxObjCount", user_info.user_quota.max_objects, formatter);
1427 encode_json("QuotaMaxBytesPerBucket", user_info.bucket_quota.max_objects, formatter);
1428 encode_json("QuotaMaxObjCountPerBucket", user_info.bucket_quota.max_size, formatter);
1429 // send info about user's capacity utilization
1430 encode_json("TotalBytes", stats.size, formatter);
1431 encode_json("TotalBytesRounded", stats.size_rounded, formatter);
1432 encode_json("TotalEntries", stats.num_objects, formatter);
1433
1434 if (s->cct->_conf->rgw_rest_getusage_op_compat) {
1435 formatter->close_section(); //Stats
1436 }
1437
1438 formatter->close_section(); // summary
1439 }
1440
1441 formatter->open_array_section("CapacityUsed");
1442 formatter->open_object_section("User");
1443 formatter->open_array_section("Buckets");
1444 for (const auto& biter : buckets_usage) {
1445 const bucket_meta_entry& entry = biter.second;
1446 dump_usage_bucket_info(formatter, biter.first, entry);
1447 }
1448 formatter->close_section(); // Buckets
1449 formatter->close_section(); // User
1450 formatter->close_section(); // CapacityUsed
1451
1452 formatter->close_section(); // usage
1453 rgw_flush_formatter_and_reset(s, s->formatter);
1454 }
1455
1456 int RGWListBucket_ObjStore_S3::get_common_params()
1457 {
1458 list_versions = s->info.args.exists("versions");
1459 prefix = s->info.args.get("prefix");
1460
1461 // non-standard
1462 s->info.args.get_bool("allow-unordered", &allow_unordered, false);
1463 delimiter = s->info.args.get("delimiter");
1464 max_keys = s->info.args.get("max-keys");
1465 op_ret = parse_max_keys();
1466 if (op_ret < 0) {
1467 return op_ret;
1468 }
1469 encoding_type = s->info.args.get("encoding-type");
1470 if (s->system_request) {
1471 s->info.args.get_bool("objs-container", &objs_container, false);
1472 const char *shard_id_str = s->info.env->get("HTTP_RGWX_SHARD_ID");
1473 if (shard_id_str) {
1474 string err;
1475 shard_id = strict_strtol(shard_id_str, 10, &err);
1476 if (!err.empty()) {
1477 ldpp_dout(this, 5) << "bad shard id specified: " << shard_id_str << dendl;
1478 return -EINVAL;
1479 }
1480 } else {
1481 shard_id = s->bucket_instance_shard_id;
1482 }
1483 }
1484 return 0;
1485 }
1486
1487 int RGWListBucket_ObjStore_S3::get_params(optional_yield y)
1488 {
1489 int ret = get_common_params();
1490 if (ret < 0) {
1491 return ret;
1492 }
1493 if (!list_versions) {
1494 marker = s->info.args.get("marker");
1495 } else {
1496 marker.name = s->info.args.get("key-marker");
1497 marker.instance = s->info.args.get("version-id-marker");
1498 }
1499 return 0;
1500 }
1501
1502 int RGWListBucket_ObjStore_S3v2::get_params(optional_yield y)
1503 {
1504 int ret = get_common_params();
1505 if (ret < 0) {
1506 return ret;
1507 }
1508 s->info.args.get_bool("fetch-owner", &fetchOwner, false);
1509 startAfter = s->info.args.get("start-after", &start_after_exist);
1510 continuation_token = s->info.args.get("continuation-token", &continuation_token_exist);
1511 if(!continuation_token_exist) {
1512 marker = startAfter;
1513 } else {
1514 marker = continuation_token;
1515 }
1516 return 0;
1517 }
1518
1519 void RGWListBucket_ObjStore_S3::send_common_versioned_response()
1520 {
1521 if (!s->bucket_tenant.empty()) {
1522 s->formatter->dump_string("Tenant", s->bucket_tenant);
1523 }
1524 s->formatter->dump_string("Name", s->bucket_name);
1525 s->formatter->dump_string("Prefix", prefix);
1526 s->formatter->dump_int("MaxKeys", max);
1527 if (!delimiter.empty()) {
1528 s->formatter->dump_string("Delimiter", delimiter);
1529 }
1530 s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
1531 : "false"));
1532
1533 if (!common_prefixes.empty()) {
1534 map<string, bool>::iterator pref_iter;
1535 for (pref_iter = common_prefixes.begin();
1536 pref_iter != common_prefixes.end(); ++pref_iter) {
1537 s->formatter->open_array_section("CommonPrefixes");
1538 if (encode_key) {
1539 s->formatter->dump_string("Prefix", url_encode(pref_iter->first, false));
1540 } else {
1541 s->formatter->dump_string("Prefix", pref_iter->first);
1542 }
1543
1544 s->formatter->close_section();
1545 }
1546 }
1547 }
1548
1549 void RGWListBucket_ObjStore_S3::send_versioned_response()
1550 {
1551 s->formatter->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3);
1552 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1553 s->formatter->dump_string("EncodingType", "url");
1554 encode_key = true;
1555 }
1556 RGWListBucket_ObjStore_S3::send_common_versioned_response();
1557 s->formatter->dump_string("KeyMarker", marker.name);
1558 s->formatter->dump_string("VersionIdMarker", marker.instance);
1559 if (is_truncated && !next_marker.empty()) {
1560 s->formatter->dump_string("NextKeyMarker", next_marker.name);
1561 if (next_marker.instance.empty()) {
1562 s->formatter->dump_string("NextVersionIdMarker", "null");
1563 }
1564 else {
1565 s->formatter->dump_string("NextVersionIdMarker", next_marker.instance);
1566 }
1567 }
1568
1569 if (op_ret >= 0) {
1570 if (objs_container) {
1571 s->formatter->open_array_section("Entries");
1572 }
1573
1574 vector<rgw_bucket_dir_entry>::iterator iter;
1575 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1576 const char *section_name = (iter->is_delete_marker() ? "DeleteMarker"
1577 : "Version");
1578 s->formatter->open_object_section(section_name);
1579 if (objs_container) {
1580 s->formatter->dump_bool("IsDeleteMarker", iter->is_delete_marker());
1581 }
1582 rgw_obj_key key(iter->key);
1583 if (encode_key) {
1584 string key_name;
1585 url_encode(key.name, key_name);
1586 s->formatter->dump_string("Key", key_name);
1587 }
1588 else {
1589 s->formatter->dump_string("Key", key.name);
1590 }
1591 string version_id = key.instance;
1592 if (version_id.empty()) {
1593 version_id = "null";
1594 }
1595 if (s->system_request) {
1596 if (iter->versioned_epoch > 0) {
1597 s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
1598 }
1599 s->formatter->dump_string("RgwxTag", iter->tag);
1600 utime_t ut(iter->meta.mtime);
1601 ut.gmtime_nsec(s->formatter->dump_stream("RgwxMtime"));
1602 }
1603 s->formatter->dump_string("VersionId", version_id);
1604 s->formatter->dump_bool("IsLatest", iter->is_current());
1605 dump_time(s, "LastModified", iter->meta.mtime);
1606 if (!iter->is_delete_marker()) {
1607 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1608 s->formatter->dump_int("Size", iter->meta.accounted_size);
1609 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1610 s->formatter->dump_string("StorageClass", storage_class.c_str());
1611 }
1612 dump_owner(s, rgw_user(iter->meta.owner), iter->meta.owner_display_name);
1613 if (iter->meta.appendable) {
1614 s->formatter->dump_string("Type", "Appendable");
1615 } else {
1616 s->formatter->dump_string("Type", "Normal");
1617 }
1618 s->formatter->close_section(); // Version/DeleteMarker
1619 }
1620 if (objs_container) {
1621 s->formatter->close_section(); // Entries
1622 }
1623 s->formatter->close_section(); // ListVersionsResult
1624 }
1625 rgw_flush_formatter_and_reset(s, s->formatter);
1626 }
1627
1628
1629 void RGWListBucket_ObjStore_S3::send_common_response()
1630 {
1631 if (!s->bucket_tenant.empty()) {
1632 s->formatter->dump_string("Tenant", s->bucket_tenant);
1633 }
1634 s->formatter->dump_string("Name", s->bucket_name);
1635 s->formatter->dump_string("Prefix", prefix);
1636 s->formatter->dump_int("MaxKeys", max);
1637 if (!delimiter.empty()) {
1638 s->formatter->dump_string("Delimiter", delimiter);
1639 }
1640 s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
1641 : "false"));
1642
1643 if (!common_prefixes.empty()) {
1644 map<string, bool>::iterator pref_iter;
1645 for (pref_iter = common_prefixes.begin();
1646 pref_iter != common_prefixes.end(); ++pref_iter) {
1647 s->formatter->open_array_section("CommonPrefixes");
1648 if (encode_key) {
1649 s->formatter->dump_string("Prefix", url_encode(pref_iter->first, false));
1650 } else {
1651 s->formatter->dump_string("Prefix", pref_iter->first);
1652 }
1653 s->formatter->close_section();
1654 }
1655 }
1656 }
1657
1658 void RGWListBucket_ObjStore_S3::send_response()
1659 {
1660 if (op_ret < 0) {
1661 set_req_state_err(s, op_ret);
1662 }
1663 dump_errno(s);
1664
1665 // Explicitly use chunked transfer encoding so that we can stream the result
1666 // to the user without having to wait for the full length of it.
1667 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
1668 dump_start(s);
1669 if (op_ret < 0) {
1670 return;
1671 }
1672 if (list_versions) {
1673 send_versioned_response();
1674 return;
1675 }
1676
1677 s->formatter->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3);
1678 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1679 s->formatter->dump_string("EncodingType", "url");
1680 encode_key = true;
1681 }
1682 RGWListBucket_ObjStore_S3::send_common_response();
1683 if (op_ret >= 0) {
1684 vector<rgw_bucket_dir_entry>::iterator iter;
1685 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1686 rgw_obj_key key(iter->key);
1687 s->formatter->open_array_section("Contents");
1688 if (encode_key) {
1689 string key_name;
1690 url_encode(key.name, key_name);
1691 s->formatter->dump_string("Key", key_name);
1692 } else {
1693 s->formatter->dump_string("Key", key.name);
1694 }
1695 dump_time(s, "LastModified", iter->meta.mtime);
1696 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1697 s->formatter->dump_int("Size", iter->meta.accounted_size);
1698 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1699 s->formatter->dump_string("StorageClass", storage_class.c_str());
1700 dump_owner(s, rgw_user(iter->meta.owner), iter->meta.owner_display_name);
1701 if (s->system_request) {
1702 s->formatter->dump_string("RgwxTag", iter->tag);
1703 }
1704 if (iter->meta.appendable) {
1705 s->formatter->dump_string("Type", "Appendable");
1706 } else {
1707 s->formatter->dump_string("Type", "Normal");
1708 }
1709 s->formatter->close_section();
1710 }
1711 }
1712 s->formatter->dump_string("Marker", marker.name);
1713 if (is_truncated && !next_marker.empty()) {
1714 s->formatter->dump_string("NextMarker", next_marker.name);
1715 }
1716 s->formatter->close_section();
1717 rgw_flush_formatter_and_reset(s, s->formatter);
1718 }
1719
1720 void RGWListBucket_ObjStore_S3v2::send_versioned_response()
1721 {
1722 s->formatter->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3);
1723 RGWListBucket_ObjStore_S3v2::send_common_versioned_response();
1724 s->formatter->dump_string("KeyContinuationToken", marker.name);
1725 s->formatter->dump_string("VersionIdContinuationToken", marker.instance);
1726 if (is_truncated && !next_marker.empty()) {
1727 s->formatter->dump_string("NextKeyContinuationToken", next_marker.name);
1728 s->formatter->dump_string("NextVersionIdContinuationToken", next_marker.instance);
1729 }
1730
1731 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1732 s->formatter->dump_string("EncodingType", "url");
1733 encode_key = true;
1734 }
1735
1736 if (op_ret >= 0) {
1737 if (objs_container) {
1738 s->formatter->open_array_section("Entries");
1739 }
1740
1741 vector<rgw_bucket_dir_entry>::iterator iter;
1742 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1743 const char *section_name = (iter->is_delete_marker() ? "DeleteContinuationToken"
1744 : "Version");
1745 s->formatter->open_object_section(section_name);
1746 if (objs_container) {
1747 s->formatter->dump_bool("IsDeleteContinuationToken", iter->is_delete_marker());
1748 }
1749 rgw_obj_key key(iter->key);
1750 if (encode_key) {
1751 string key_name;
1752 url_encode(key.name, key_name);
1753 s->formatter->dump_string("Key", key_name);
1754 }
1755 else {
1756 s->formatter->dump_string("Key", key.name);
1757 }
1758 string version_id = key.instance;
1759 if (version_id.empty()) {
1760 version_id = "null";
1761 }
1762 if (s->system_request) {
1763 if (iter->versioned_epoch > 0) {
1764 s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
1765 }
1766 s->formatter->dump_string("RgwxTag", iter->tag);
1767 utime_t ut(iter->meta.mtime);
1768 ut.gmtime_nsec(s->formatter->dump_stream("RgwxMtime"));
1769 }
1770 s->formatter->dump_string("VersionId", version_id);
1771 s->formatter->dump_bool("IsLatest", iter->is_current());
1772 dump_time(s, "LastModified", iter->meta.mtime);
1773 if (!iter->is_delete_marker()) {
1774 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1775 s->formatter->dump_int("Size", iter->meta.accounted_size);
1776 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1777 s->formatter->dump_string("StorageClass", storage_class.c_str());
1778 }
1779 if (fetchOwner == true) {
1780 dump_owner(s, s->user->get_id(), s->user->get_display_name());
1781 }
1782 s->formatter->close_section();
1783 }
1784
1785
1786 if (objs_container) {
1787 s->formatter->close_section();
1788 }
1789
1790 if (!common_prefixes.empty()) {
1791 map<string, bool>::iterator pref_iter;
1792 for (pref_iter = common_prefixes.begin();
1793 pref_iter != common_prefixes.end(); ++pref_iter) {
1794 s->formatter->open_array_section("CommonPrefixes");
1795 if (encode_key) {
1796 s->formatter->dump_string("Prefix", url_encode(pref_iter->first, false));
1797 } else {
1798 s->formatter->dump_string("Prefix", pref_iter->first);
1799 }
1800
1801 s->formatter->dump_int("KeyCount",objs.size());
1802 if (start_after_exist) {
1803 s->formatter->dump_string("StartAfter", startAfter);
1804 }
1805 s->formatter->close_section();
1806 }
1807 }
1808
1809 s->formatter->close_section();
1810 rgw_flush_formatter_and_reset(s, s->formatter);
1811 }
1812 }
1813
1814 void RGWListBucket_ObjStore_S3v2::send_response()
1815 {
1816 if (op_ret < 0) {
1817 set_req_state_err(s, op_ret);
1818 }
1819 dump_errno(s);
1820
1821 // Explicitly use chunked transfer encoding so that we can stream the result
1822 // to the user without having to wait for the full length of it.
1823 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
1824 dump_start(s);
1825 if (op_ret < 0) {
1826 return;
1827 }
1828 if (list_versions) {
1829 send_versioned_response();
1830 return;
1831 }
1832
1833 s->formatter->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3);
1834 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1835 s->formatter->dump_string("EncodingType", "url");
1836 encode_key = true;
1837 }
1838
1839 RGWListBucket_ObjStore_S3::send_common_response();
1840 if (op_ret >= 0) {
1841 vector<rgw_bucket_dir_entry>::iterator iter;
1842 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1843 rgw_obj_key key(iter->key);
1844 s->formatter->open_array_section("Contents");
1845 if (encode_key) {
1846 string key_name;
1847 url_encode(key.name, key_name);
1848 s->formatter->dump_string("Key", key_name);
1849 }
1850 else {
1851 s->formatter->dump_string("Key", key.name);
1852 }
1853 dump_time(s, "LastModified", iter->meta.mtime);
1854 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1855 s->formatter->dump_int("Size", iter->meta.accounted_size);
1856 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1857 s->formatter->dump_string("StorageClass", storage_class.c_str());
1858 if (fetchOwner == true) {
1859 dump_owner(s, s->user->get_id(), s->user->get_display_name());
1860 }
1861 if (s->system_request) {
1862 s->formatter->dump_string("RgwxTag", iter->tag);
1863 }
1864 if (iter->meta.appendable) {
1865 s->formatter->dump_string("Type", "Appendable");
1866 } else {
1867 s->formatter->dump_string("Type", "Normal");
1868 }
1869 s->formatter->close_section();
1870 }
1871 }
1872 if (continuation_token_exist) {
1873 s->formatter->dump_string("ContinuationToken", continuation_token);
1874 }
1875 if (is_truncated && !next_marker.empty()) {
1876 s->formatter->dump_string("NextContinuationToken", next_marker.name);
1877 }
1878 s->formatter->dump_int("KeyCount", objs.size() + common_prefixes.size());
1879 if (start_after_exist) {
1880 s->formatter->dump_string("StartAfter", startAfter);
1881 }
1882 s->formatter->close_section();
1883 rgw_flush_formatter_and_reset(s, s->formatter);
1884 }
1885
1886 void RGWGetBucketLogging_ObjStore_S3::send_response()
1887 {
1888 dump_errno(s);
1889 end_header(s, this, "application/xml");
1890 dump_start(s);
1891
1892 s->formatter->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3);
1893 s->formatter->close_section();
1894 rgw_flush_formatter_and_reset(s, s->formatter);
1895 }
1896
1897 void RGWGetBucketLocation_ObjStore_S3::send_response()
1898 {
1899 dump_errno(s);
1900 end_header(s, this);
1901 dump_start(s);
1902
1903 RGWZoneGroup zonegroup;
1904 string api_name;
1905
1906 int ret = store->get_zone()->get_zonegroup(s->bucket->get_info().zonegroup, zonegroup);
1907 if (ret >= 0) {
1908 api_name = zonegroup.api_name;
1909 } else {
1910 if (s->bucket->get_info().zonegroup != "default") {
1911 api_name = s->bucket->get_info().zonegroup;
1912 }
1913 }
1914
1915 s->formatter->dump_format_ns("LocationConstraint", XMLNS_AWS_S3,
1916 "%s", api_name.c_str());
1917 rgw_flush_formatter_and_reset(s, s->formatter);
1918 }
1919
1920 void RGWGetBucketVersioning_ObjStore_S3::send_response()
1921 {
1922 if (op_ret)
1923 set_req_state_err(s, op_ret);
1924 dump_errno(s);
1925 end_header(s, this, "application/xml");
1926 dump_start(s);
1927
1928 s->formatter->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3);
1929 if (versioned) {
1930 const char *status = (versioning_enabled ? "Enabled" : "Suspended");
1931 s->formatter->dump_string("Status", status);
1932 const char *mfa_status = (mfa_enabled ? "Enabled" : "Disabled");
1933 s->formatter->dump_string("MfaDelete", mfa_status);
1934 }
1935 s->formatter->close_section();
1936 rgw_flush_formatter_and_reset(s, s->formatter);
1937 }
1938
1939 struct ver_config_status {
1940 int status{VersioningSuspended};
1941
1942 enum MFAStatus {
1943 MFA_UNKNOWN,
1944 MFA_DISABLED,
1945 MFA_ENABLED,
1946 } mfa_status{MFA_UNKNOWN};
1947 int retcode{0};
1948
1949 void decode_xml(XMLObj *obj) {
1950 string status_str;
1951 string mfa_str;
1952 RGWXMLDecoder::decode_xml("Status", status_str, obj);
1953 if (status_str == "Enabled") {
1954 status = VersioningEnabled;
1955 } else if (status_str != "Suspended") {
1956 status = VersioningStatusInvalid;
1957 }
1958
1959
1960 if (RGWXMLDecoder::decode_xml("MfaDelete", mfa_str, obj)) {
1961 if (mfa_str == "Enabled") {
1962 mfa_status = MFA_ENABLED;
1963 } else if (mfa_str == "Disabled") {
1964 mfa_status = MFA_DISABLED;
1965 } else {
1966 retcode = -EINVAL;
1967 }
1968 }
1969 }
1970 };
1971
1972 int RGWSetBucketVersioning_ObjStore_S3::get_params(optional_yield y)
1973 {
1974 int r = 0;
1975 bufferlist data;
1976 std::tie(r, data) =
1977 read_all_input(s, s->cct->_conf->rgw_max_put_param_size, false);
1978 if (r < 0) {
1979 return r;
1980 }
1981
1982 RGWXMLDecoder::XMLParser parser;
1983 if (!parser.init()) {
1984 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
1985 return -EIO;
1986 }
1987
1988 char* buf = data.c_str();
1989 if (!parser.parse(buf, data.length(), 1)) {
1990 ldpp_dout(this, 10) << "NOTICE: failed to parse data: " << buf << dendl;
1991 r = -EINVAL;
1992 return r;
1993 }
1994
1995 ver_config_status status_conf;
1996
1997 if (!RGWXMLDecoder::decode_xml("VersioningConfiguration", status_conf, &parser)) {
1998 ldpp_dout(this, 10) << "NOTICE: bad versioning config input" << dendl;
1999 return -EINVAL;
2000 }
2001
2002 if (!store->is_meta_master()) {
2003 /* only need to keep this data around if we're not meta master */
2004 in_data.append(data);
2005 }
2006
2007 versioning_status = status_conf.status;
2008 if (versioning_status == VersioningStatusInvalid) {
2009 r = -EINVAL;
2010 }
2011
2012 if (status_conf.mfa_status != ver_config_status::MFA_UNKNOWN) {
2013 mfa_set_status = true;
2014 switch (status_conf.mfa_status) {
2015 case ver_config_status::MFA_DISABLED:
2016 mfa_status = false;
2017 break;
2018 case ver_config_status::MFA_ENABLED:
2019 mfa_status = true;
2020 break;
2021 default:
2022 ldpp_dout(this, 0) << "ERROR: RGWSetBucketVersioning_ObjStore_S3::get_params(optional_yield y): unexpected switch case mfa_status=" << status_conf.mfa_status << dendl;
2023 r = -EIO;
2024 }
2025 } else if (status_conf.retcode < 0) {
2026 r = status_conf.retcode;
2027 }
2028 return r;
2029 }
2030
2031 void RGWSetBucketVersioning_ObjStore_S3::send_response()
2032 {
2033 if (op_ret)
2034 set_req_state_err(s, op_ret);
2035 dump_errno(s);
2036 end_header(s, this, "application/xml");
2037 }
2038
2039 int RGWSetBucketWebsite_ObjStore_S3::get_params(optional_yield y)
2040 {
2041 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
2042
2043 int r = 0;
2044 bufferlist data;
2045 std::tie(r, data) = read_all_input(s, max_size, false);
2046
2047 if (r < 0) {
2048 return r;
2049 }
2050
2051 in_data.append(data);
2052
2053 RGWXMLDecoder::XMLParser parser;
2054 if (!parser.init()) {
2055 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
2056 return -EIO;
2057 }
2058
2059 char* buf = data.c_str();
2060 if (!parser.parse(buf, data.length(), 1)) {
2061 ldpp_dout(this, 5) << "failed to parse xml: " << buf << dendl;
2062 return -EINVAL;
2063 }
2064
2065 try {
2066 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf, &parser, true);
2067 } catch (RGWXMLDecoder::err& err) {
2068 ldpp_dout(this, 5) << "unexpected xml: " << buf << dendl;
2069 return -EINVAL;
2070 }
2071
2072 if (website_conf.is_redirect_all && website_conf.redirect_all.hostname.empty()) {
2073 s->err.message = "A host name must be provided to redirect all requests (e.g. \"example.com\").";
2074 ldpp_dout(this, 5) << s->err.message << dendl;
2075 return -EINVAL;
2076 } else if (!website_conf.is_redirect_all && !website_conf.is_set_index_doc) {
2077 s->err.message = "A value for IndexDocument Suffix must be provided if RedirectAllRequestsTo is empty";
2078 ldpp_dout(this, 5) << s->err.message << dendl;
2079 return -EINVAL;
2080 } else if (!website_conf.is_redirect_all && website_conf.is_set_index_doc &&
2081 website_conf.index_doc_suffix.empty()) {
2082 s->err.message = "The IndexDocument Suffix is not well formed";
2083 ldpp_dout(this, 5) << s->err.message << dendl;
2084 return -EINVAL;
2085 }
2086
2087 #define WEBSITE_ROUTING_RULES_MAX_NUM 50
2088 int max_num = s->cct->_conf->rgw_website_routing_rules_max_num;
2089 if (max_num < 0) {
2090 max_num = WEBSITE_ROUTING_RULES_MAX_NUM;
2091 }
2092 int routing_rules_num = website_conf.routing_rules.rules.size();
2093 if (routing_rules_num > max_num) {
2094 ldpp_dout(this, 4) << "An website routing config can have up to "
2095 << max_num
2096 << " rules, request website routing rules num: "
2097 << routing_rules_num << dendl;
2098 op_ret = -ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR;
2099 s->err.message = std::to_string(routing_rules_num) +" routing rules provided, the number of routing rules in a website configuration is limited to "
2100 + std::to_string(max_num)
2101 + ".";
2102 return -ERR_INVALID_REQUEST;
2103 }
2104
2105 return 0;
2106 }
2107
2108 void RGWSetBucketWebsite_ObjStore_S3::send_response()
2109 {
2110 if (op_ret < 0)
2111 set_req_state_err(s, op_ret);
2112 dump_errno(s);
2113 end_header(s, this, "application/xml");
2114 }
2115
2116 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
2117 {
2118 if (op_ret == 0) {
2119 op_ret = STATUS_NO_CONTENT;
2120 }
2121 set_req_state_err(s, op_ret);
2122 dump_errno(s);
2123 end_header(s, this, "application/xml");
2124 }
2125
2126 void RGWGetBucketWebsite_ObjStore_S3::send_response()
2127 {
2128 if (op_ret)
2129 set_req_state_err(s, op_ret);
2130 dump_errno(s);
2131 end_header(s, this, "application/xml");
2132 dump_start(s);
2133
2134 if (op_ret < 0) {
2135 return;
2136 }
2137
2138 RGWBucketWebsiteConf& conf = s->bucket->get_info().website_conf;
2139
2140 s->formatter->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3);
2141 conf.dump_xml(s->formatter);
2142 s->formatter->close_section(); // WebsiteConfiguration
2143 rgw_flush_formatter_and_reset(s, s->formatter);
2144 }
2145
2146 static void dump_bucket_metadata(struct req_state *s, rgw::sal::Bucket* bucket)
2147 {
2148 dump_header(s, "X-RGW-Object-Count", static_cast<long long>(bucket->get_count()));
2149 dump_header(s, "X-RGW-Bytes-Used", static_cast<long long>(bucket->get_size()));
2150 // only bucket's owner is allowed to get the quota settings of the account
2151 if (bucket->is_owner(s->user.get())) {
2152 auto user_info = s->user->get_info();
2153 dump_header(s, "X-RGW-Quota-User-Size", static_cast<long long>(user_info.user_quota.max_size));
2154 dump_header(s, "X-RGW-Quota-User-Objects", static_cast<long long>(user_info.user_quota.max_objects));
2155 dump_header(s, "X-RGW-Quota-Max-Buckets", static_cast<long long>(user_info.max_buckets));
2156 dump_header(s, "X-RGW-Quota-Bucket-Size", static_cast<long long>(user_info.bucket_quota.max_size));
2157 dump_header(s, "X-RGW-Quota-Bucket-Objects", static_cast<long long>(user_info.bucket_quota.max_objects));
2158 }
2159 }
2160
2161 void RGWStatBucket_ObjStore_S3::send_response()
2162 {
2163 if (op_ret >= 0) {
2164 dump_bucket_metadata(s, bucket.get());
2165 }
2166
2167 set_req_state_err(s, op_ret);
2168 dump_errno(s);
2169
2170 end_header(s, this);
2171 dump_start(s);
2172 }
2173
2174 static int create_s3_policy(struct req_state *s, rgw::sal::Store* store,
2175 RGWAccessControlPolicy_S3& s3policy,
2176 ACLOwner& owner)
2177 {
2178 if (s->has_acl_header) {
2179 if (!s->canned_acl.empty())
2180 return -ERR_INVALID_REQUEST;
2181
2182 return s3policy.create_from_headers(s, store, s->info.env, owner);
2183 }
2184
2185 return s3policy.create_canned(owner, s->bucket_owner, s->canned_acl);
2186 }
2187
2188 class RGWLocationConstraint : public XMLObj
2189 {
2190 public:
2191 RGWLocationConstraint() {}
2192 ~RGWLocationConstraint() override {}
2193 bool xml_end(const char *el) override {
2194 if (!el)
2195 return false;
2196
2197 location_constraint = get_data();
2198
2199 return true;
2200 }
2201
2202 string location_constraint;
2203 };
2204
2205 class RGWCreateBucketConfig : public XMLObj
2206 {
2207 public:
2208 RGWCreateBucketConfig() {}
2209 ~RGWCreateBucketConfig() override {}
2210 };
2211
2212 class RGWCreateBucketParser : public RGWXMLParser
2213 {
2214 XMLObj *alloc_obj(const char *el) override {
2215 return new XMLObj;
2216 }
2217
2218 public:
2219 RGWCreateBucketParser() {}
2220 ~RGWCreateBucketParser() override {}
2221
2222 bool get_location_constraint(string& zone_group) {
2223 XMLObj *config = find_first("CreateBucketConfiguration");
2224 if (!config)
2225 return false;
2226
2227 XMLObj *constraint = config->find_first("LocationConstraint");
2228 if (!constraint)
2229 return false;
2230
2231 zone_group = constraint->get_data();
2232
2233 return true;
2234 }
2235 };
2236
2237 int RGWCreateBucket_ObjStore_S3::get_params(optional_yield y)
2238 {
2239 RGWAccessControlPolicy_S3 s3policy(s->cct);
2240 bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
2241
2242 int r;
2243 if (!s->system_request) {
2244 r = valid_s3_bucket_name(s->bucket_name, relaxed_names);
2245 if (r) return r;
2246 }
2247
2248 r = create_s3_policy(s, store, s3policy, s->owner);
2249 if (r < 0)
2250 return r;
2251
2252 policy = s3policy;
2253
2254 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
2255
2256 int op_ret = 0;
2257 bufferlist data;
2258 std::tie(op_ret, data) = read_all_input(s, max_size, false);
2259
2260 if ((op_ret < 0) && (op_ret != -ERR_LENGTH_REQUIRED))
2261 return op_ret;
2262
2263 in_data.append(data);
2264
2265 if (data.length()) {
2266 RGWCreateBucketParser parser;
2267
2268 if (!parser.init()) {
2269 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
2270 return -EIO;
2271 }
2272
2273 char* buf = data.c_str();
2274 bool success = parser.parse(buf, data.length(), 1);
2275 ldpp_dout(this, 20) << "create bucket input data=" << buf << dendl;
2276
2277 if (!success) {
2278 ldpp_dout(this, 0) << "failed to parse input: " << buf << dendl;
2279 return -EINVAL;
2280 }
2281
2282 if (!parser.get_location_constraint(location_constraint)) {
2283 ldpp_dout(this, 0) << "provided input did not specify location constraint correctly" << dendl;
2284 return -EINVAL;
2285 }
2286
2287 ldpp_dout(this, 10) << "create bucket location constraint: "
2288 << location_constraint << dendl;
2289 }
2290
2291 size_t pos = location_constraint.find(':');
2292 if (pos != string::npos) {
2293 placement_rule.init(location_constraint.substr(pos + 1), s->info.storage_class);
2294 location_constraint = location_constraint.substr(0, pos);
2295 } else {
2296 placement_rule.storage_class = s->info.storage_class;
2297 }
2298 auto iter = s->info.x_meta_map.find("x-amz-bucket-object-lock-enabled");
2299 if (iter != s->info.x_meta_map.end()) {
2300 if (!boost::algorithm::iequals(iter->second, "true") && !boost::algorithm::iequals(iter->second, "false")) {
2301 return -EINVAL;
2302 }
2303 obj_lock_enabled = boost::algorithm::iequals(iter->second, "true");
2304 }
2305 return 0;
2306 }
2307
2308 void RGWCreateBucket_ObjStore_S3::send_response()
2309 {
2310 if (op_ret == -ERR_BUCKET_EXISTS)
2311 op_ret = 0;
2312 if (op_ret)
2313 set_req_state_err(s, op_ret);
2314 dump_errno(s);
2315 end_header(s);
2316
2317 if (op_ret < 0)
2318 return;
2319
2320 if (s->system_request) {
2321 JSONFormatter f; /* use json formatter for system requests output */
2322
2323 f.open_object_section("info");
2324 encode_json("entry_point_object_ver", ep_objv, &f);
2325 encode_json("object_ver", info.objv_tracker.read_version, &f);
2326 encode_json("bucket_info", info, &f);
2327 f.close_section();
2328 rgw_flush_formatter_and_reset(s, &f);
2329 }
2330 }
2331
2332 void RGWDeleteBucket_ObjStore_S3::send_response()
2333 {
2334 int r = op_ret;
2335 if (!r)
2336 r = STATUS_NO_CONTENT;
2337
2338 set_req_state_err(s, r);
2339 dump_errno(s);
2340 end_header(s, this);
2341 }
2342
2343 static inline void map_qs_metadata(struct req_state* s)
2344 {
2345 /* merge S3 valid user metadata from the query-string into
2346 * x_meta_map, which maps them to attributes */
2347 const auto& params = const_cast<RGWHTTPArgs&>(s->info.args).get_params();
2348 for (const auto& elt : params) {
2349 std::string k = boost::algorithm::to_lower_copy(elt.first);
2350 if (k.find("x-amz-meta-") == /* offset */ 0) {
2351 rgw_add_amz_meta_header(s->info.x_meta_map, k, elt.second);
2352 }
2353 }
2354 }
2355
2356 int RGWPutObj_ObjStore_S3::get_params(optional_yield y)
2357 {
2358 if (!s->length)
2359 return -ERR_LENGTH_REQUIRED;
2360
2361 int ret;
2362
2363 map_qs_metadata(s);
2364
2365 RGWAccessControlPolicy_S3 s3policy(s->cct);
2366 ret = create_s3_policy(s, store, s3policy, s->owner);
2367 if (ret < 0)
2368 return ret;
2369
2370 policy = s3policy;
2371
2372 if_match = s->info.env->get("HTTP_IF_MATCH");
2373 if_nomatch = s->info.env->get("HTTP_IF_NONE_MATCH");
2374
2375 /* handle object tagging */
2376 auto tag_str = s->info.env->get("HTTP_X_AMZ_TAGGING");
2377 if (tag_str){
2378 obj_tags = std::make_unique<RGWObjTags>();
2379 ret = obj_tags->set_from_string(tag_str);
2380 if (ret < 0){
2381 ldpp_dout(this,0) << "setting obj tags failed with " << ret << dendl;
2382 if (ret == -ERR_INVALID_TAG){
2383 ret = -EINVAL; //s3 returns only -EINVAL for PUT requests
2384 }
2385
2386 return ret;
2387 }
2388 }
2389
2390 //handle object lock
2391 auto obj_lock_mode_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_MODE");
2392 auto obj_lock_date_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE");
2393 auto obj_legal_hold_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_LEGAL_HOLD");
2394 if (obj_lock_mode_str && obj_lock_date_str) {
2395 boost::optional<ceph::real_time> date = ceph::from_iso_8601(obj_lock_date_str);
2396 if (boost::none == date || ceph::real_clock::to_time_t(*date) <= ceph_clock_now()) {
2397 ret = -EINVAL;
2398 ldpp_dout(this,0) << "invalid x-amz-object-lock-retain-until-date value" << dendl;
2399 return ret;
2400 }
2401 if (strcmp(obj_lock_mode_str, "GOVERNANCE") != 0 && strcmp(obj_lock_mode_str, "COMPLIANCE") != 0) {
2402 ret = -EINVAL;
2403 ldpp_dout(this,0) << "invalid x-amz-object-lock-mode value" << dendl;
2404 return ret;
2405 }
2406 obj_retention = new RGWObjectRetention(obj_lock_mode_str, *date);
2407 } else if ((obj_lock_mode_str && !obj_lock_date_str) || (!obj_lock_mode_str && obj_lock_date_str)) {
2408 ret = -EINVAL;
2409 ldpp_dout(this,0) << "need both x-amz-object-lock-mode and x-amz-object-lock-retain-until-date " << dendl;
2410 return ret;
2411 }
2412 if (obj_legal_hold_str) {
2413 if (strcmp(obj_legal_hold_str, "ON") != 0 && strcmp(obj_legal_hold_str, "OFF") != 0) {
2414 ret = -EINVAL;
2415 ldpp_dout(this,0) << "invalid x-amz-object-lock-legal-hold value" << dendl;
2416 return ret;
2417 }
2418 obj_legal_hold = new RGWObjectLegalHold(obj_legal_hold_str);
2419 }
2420 if (!s->bucket->get_info().obj_lock_enabled() && (obj_retention || obj_legal_hold)) {
2421 ldpp_dout(this, 0) << "ERROR: object retention or legal hold can't be set if bucket object lock not configured" << dendl;
2422 ret = -ERR_INVALID_REQUEST;
2423 return ret;
2424 }
2425 multipart_upload_id = s->info.args.get("uploadId");
2426 multipart_part_str = s->info.args.get("partNumber");
2427 if (!multipart_part_str.empty()) {
2428 string err;
2429 multipart_part_num = strict_strtol(multipart_part_str.c_str(), 10, &err);
2430 if (!err.empty()) {
2431 ldpp_dout(s, 10) << "bad part number: " << multipart_part_str << ": " << err << dendl;
2432 return -EINVAL;
2433 }
2434 } else if (!multipart_upload_id.empty()) {
2435 ldpp_dout(s, 10) << "part number with no multipart upload id" << dendl;
2436 return -EINVAL;
2437 }
2438
2439 append = s->info.args.exists("append");
2440 if (append) {
2441 string pos_str = s->info.args.get("position");
2442 string err;
2443 long long pos_tmp = strict_strtoll(pos_str.c_str(), 10, &err);
2444 if (!err.empty()) {
2445 ldpp_dout(s, 10) << "bad position: " << pos_str << ": " << err << dendl;
2446 return -EINVAL;
2447 } else if (pos_tmp < 0) {
2448 ldpp_dout(s, 10) << "bad position: " << pos_str << ": " << "position shouldn't be negative" << dendl;
2449 return -EINVAL;
2450 }
2451 position = uint64_t(pos_tmp);
2452 }
2453
2454 return RGWPutObj_ObjStore::get_params(y);
2455 }
2456
2457 int RGWPutObj_ObjStore_S3::get_data(bufferlist& bl)
2458 {
2459 const int ret = RGWPutObj_ObjStore::get_data(bl);
2460 if (ret == 0) {
2461 const int ret_auth = do_aws4_auth_completion();
2462 if (ret_auth < 0) {
2463 return ret_auth;
2464 }
2465 }
2466
2467 return ret;
2468 }
2469
2470 static int get_success_retcode(int code)
2471 {
2472 switch (code) {
2473 case 201:
2474 return STATUS_CREATED;
2475 case 204:
2476 return STATUS_NO_CONTENT;
2477 }
2478 return 0;
2479 }
2480
2481 void RGWPutObj_ObjStore_S3::send_response()
2482 {
2483 if (op_ret) {
2484 set_req_state_err(s, op_ret);
2485 dump_errno(s);
2486 } else {
2487 if (s->cct->_conf->rgw_s3_success_create_obj_status) {
2488 op_ret = get_success_retcode(
2489 s->cct->_conf->rgw_s3_success_create_obj_status);
2490 set_req_state_err(s, op_ret);
2491 }
2492
2493 string expires = get_s3_expiration_header(s, mtime);
2494
2495 if (copy_source.empty()) {
2496 dump_errno(s);
2497 dump_etag(s, etag);
2498 dump_content_length(s, 0);
2499 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
2500 dump_header_if_nonempty(s, "x-amz-expiration", expires);
2501 for (auto &it : crypt_http_responses)
2502 dump_header(s, it.first, it.second);
2503 } else {
2504 dump_errno(s);
2505 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
2506 dump_header_if_nonempty(s, "x-amz-expiration", expires);
2507 end_header(s, this, "application/xml");
2508 dump_start(s);
2509 struct tm tmp;
2510 utime_t ut(mtime);
2511 time_t secs = (time_t)ut.sec();
2512 gmtime_r(&secs, &tmp);
2513 char buf[TIME_BUF_SIZE];
2514 s->formatter->open_object_section_in_ns("CopyPartResult",
2515 "http://s3.amazonaws.com/doc/2006-03-01/");
2516 if (strftime(buf, sizeof(buf), "%Y-%m-%dT%T.000Z", &tmp) > 0) {
2517 s->formatter->dump_string("LastModified", buf);
2518 }
2519 s->formatter->dump_string("ETag", etag);
2520 s->formatter->close_section();
2521 rgw_flush_formatter_and_reset(s, s->formatter);
2522 return;
2523 }
2524 }
2525 if (append) {
2526 if (op_ret == 0 || op_ret == -ERR_POSITION_NOT_EQUAL_TO_LENGTH) {
2527 dump_header(s, "x-rgw-next-append-position", cur_accounted_size);
2528 }
2529 }
2530 if (s->system_request && !real_clock::is_zero(mtime)) {
2531 dump_epoch_header(s, "Rgwx-Mtime", mtime);
2532 }
2533 end_header(s, this);
2534 }
2535
2536 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const std::string& value)
2537 {
2538 bufferlist bl;
2539 encode(value,bl);
2540 attrs.emplace(key, std::move(bl));
2541 }
2542
2543 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const char* value)
2544 {
2545 bufferlist bl;
2546 encode(value,bl);
2547 attrs.emplace(key, std::move(bl));
2548 }
2549
2550 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
2551 std::unique_ptr<RGWGetObj_Filter>* filter,
2552 RGWGetObj_Filter* cb,
2553 map<string, bufferlist>& attrs,
2554 bufferlist* manifest_bl)
2555 {
2556 std::map<std::string, std::string> crypt_http_responses_unused;
2557
2558 int res = 0;
2559 std::unique_ptr<BlockCrypt> block_crypt;
2560 res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses_unused);
2561 if (res == 0) {
2562 if (block_crypt != nullptr) {
2563 auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s, s->cct, cb, std::move(block_crypt)));
2564 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
2565 if (f != nullptr) {
2566 if (manifest_bl != nullptr) {
2567 res = f->read_manifest(this, *manifest_bl);
2568 if (res == 0) {
2569 *filter = std::move(f);
2570 }
2571 }
2572 }
2573 }
2574 }
2575 return res;
2576 }
2577
2578 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
2579 std::unique_ptr<rgw::sal::DataProcessor> *filter,
2580 rgw::sal::DataProcessor *cb)
2581 {
2582 int res = 0;
2583 if (!multipart_upload_id.empty()) {
2584 std::unique_ptr<rgw::sal::MultipartUpload> upload =
2585 s->bucket->get_multipart_upload(s->object->get_name(),
2586 multipart_upload_id);
2587 std::unique_ptr<rgw::sal::Object> obj = upload->get_meta_obj();
2588 obj->set_in_extra_data(true);
2589 res = obj->get_obj_attrs(s->obj_ctx, s->yield, this);
2590 if (res == 0) {
2591 std::unique_ptr<BlockCrypt> block_crypt;
2592 /* We are adding to existing object.
2593 * We use crypto mode that configured as if we were decrypting. */
2594 res = rgw_s3_prepare_decrypt(s, obj->get_attrs(), &block_crypt, crypt_http_responses);
2595 if (res == 0 && block_crypt != nullptr)
2596 filter->reset(new RGWPutObj_BlockEncrypt(s, s->cct, cb, std::move(block_crypt)));
2597 }
2598 /* it is ok, to not have encryption at all */
2599 }
2600 else
2601 {
2602 std::unique_ptr<BlockCrypt> block_crypt;
2603 res = rgw_s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses);
2604 if (res == 0 && block_crypt != nullptr) {
2605 filter->reset(new RGWPutObj_BlockEncrypt(s, s->cct, cb, std::move(block_crypt)));
2606 }
2607 }
2608 return res;
2609 }
2610
2611 void RGWPostObj_ObjStore_S3::rebuild_key(rgw::sal::Object* obj)
2612 {
2613 string key = obj->get_name();
2614 static string var = "${filename}";
2615 int pos = key.find(var);
2616 if (pos < 0)
2617 return;
2618
2619 string new_key = key.substr(0, pos);
2620 new_key.append(filename);
2621 new_key.append(key.substr(pos + var.size()));
2622
2623 obj->set_key(new_key);
2624 }
2625
2626 std::string RGWPostObj_ObjStore_S3::get_current_filename() const
2627 {
2628 return s->object->get_name();
2629 }
2630
2631 std::string RGWPostObj_ObjStore_S3::get_current_content_type() const
2632 {
2633 return content_type;
2634 }
2635
2636 int RGWPostObj_ObjStore_S3::get_params(optional_yield y)
2637 {
2638 op_ret = RGWPostObj_ObjStore::get_params(y);
2639 if (op_ret < 0) {
2640 return op_ret;
2641 }
2642
2643 map_qs_metadata(s);
2644
2645 ldpp_dout(this, 20) << "adding bucket to policy env: " << s->bucket->get_name()
2646 << dendl;
2647 env.add_var("bucket", s->bucket->get_name());
2648
2649 bool done;
2650 do {
2651 struct post_form_part part;
2652 int r = read_form_part_header(&part, done);
2653 if (r < 0)
2654 return r;
2655
2656 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
2657 ldpp_dout(this, 20) << "read part header -- part.name="
2658 << part.name << dendl;
2659
2660 for (const auto& pair : part.fields) {
2661 ldpp_dout(this, 20) << "field.name=" << pair.first << dendl;
2662 ldpp_dout(this, 20) << "field.val=" << pair.second.val << dendl;
2663 ldpp_dout(this, 20) << "field.params:" << dendl;
2664
2665 for (const auto& param_pair : pair.second.params) {
2666 ldpp_dout(this, 20) << " " << param_pair.first
2667 << " -> " << param_pair.second << dendl;
2668 }
2669 }
2670 }
2671
2672 if (done) { /* unexpected here */
2673 err_msg = "Malformed request";
2674 return -EINVAL;
2675 }
2676
2677 if (stringcasecmp(part.name, "file") == 0) { /* beginning of data transfer */
2678 struct post_part_field& field = part.fields["Content-Disposition"];
2679 map<string, string>::iterator iter = field.params.find("filename");
2680 if (iter != field.params.end()) {
2681 filename = iter->second;
2682 }
2683 parts[part.name] = part;
2684 break;
2685 }
2686
2687 bool boundary;
2688 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
2689 r = read_data(part.data, chunk_size, boundary, done);
2690 if (r < 0 || !boundary) {
2691 err_msg = "Couldn't find boundary";
2692 return -EINVAL;
2693 }
2694 parts[part.name] = part;
2695 string part_str(part.data.c_str(), part.data.length());
2696 env.add_var(part.name, part_str);
2697 } while (!done);
2698
2699 string object_str;
2700 if (!part_str(parts, "key", &object_str)) {
2701 err_msg = "Key not specified";
2702 return -EINVAL;
2703 }
2704
2705 s->object = store->get_object(rgw_obj_key(object_str));
2706
2707 rebuild_key(s->object.get());
2708
2709 if (rgw::sal::Object::empty(s->object.get())) {
2710 err_msg = "Empty object name";
2711 return -EINVAL;
2712 }
2713
2714 env.add_var("key", s->object->get_name());
2715
2716 part_str(parts, "Content-Type", &content_type);
2717
2718 /* AWS permits POST without Content-Type: http://tracker.ceph.com/issues/20201 */
2719 if (! content_type.empty()) {
2720 env.add_var("Content-Type", content_type);
2721 }
2722
2723 std::string storage_class;
2724 part_str(parts, "x-amz-storage-class", &storage_class);
2725
2726 if (! storage_class.empty()) {
2727 s->dest_placement.storage_class = storage_class;
2728 if (!store->get_zone()->get_params().valid_placement(s->dest_placement)) {
2729 ldpp_dout(this, 0) << "NOTICE: invalid dest placement: " << s->dest_placement.to_str() << dendl;
2730 err_msg = "The storage class you specified is not valid";
2731 return -EINVAL;
2732 }
2733 }
2734
2735 map<string, struct post_form_part, ltstr_nocase>::iterator piter =
2736 parts.upper_bound(RGW_AMZ_META_PREFIX);
2737 for (; piter != parts.end(); ++piter) {
2738 string n = piter->first;
2739 if (strncasecmp(n.c_str(), RGW_AMZ_META_PREFIX,
2740 sizeof(RGW_AMZ_META_PREFIX) - 1) != 0)
2741 break;
2742
2743 string attr_name = RGW_ATTR_PREFIX;
2744 attr_name.append(n);
2745
2746 /* need to null terminate it */
2747 bufferlist& data = piter->second.data;
2748 string str = string(data.c_str(), data.length());
2749
2750 bufferlist attr_bl;
2751 attr_bl.append(str.c_str(), str.size() + 1);
2752
2753 attrs[attr_name] = attr_bl;
2754 }
2755 // TODO: refactor this and the above loop to share code
2756 piter = parts.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION);
2757 if (piter != parts.end()) {
2758 string n = piter->first;
2759 string attr_name = RGW_ATTR_PREFIX;
2760 attr_name.append(n);
2761 /* need to null terminate it */
2762 bufferlist& data = piter->second.data;
2763 string str = string(data.c_str(), data.length());
2764
2765 bufferlist attr_bl;
2766 attr_bl.append(str.c_str(), str.size() + 1);
2767
2768 attrs[attr_name] = attr_bl;
2769 }
2770
2771 int r = get_policy(y);
2772 if (r < 0)
2773 return r;
2774
2775 r = get_tags();
2776 if (r < 0)
2777 return r;
2778
2779
2780 min_len = post_policy.min_length;
2781 max_len = post_policy.max_length;
2782
2783
2784
2785 return 0;
2786 }
2787
2788 int RGWPostObj_ObjStore_S3::get_tags()
2789 {
2790 string tags_str;
2791 if (part_str(parts, "tagging", &tags_str)) {
2792 RGWXMLParser parser;
2793 if (!parser.init()){
2794 ldpp_dout(this, 0) << "Couldn't init RGWObjTags XML parser" << dendl;
2795 err_msg = "Server couldn't process the request";
2796 return -EINVAL; // TODO: This class of errors in rgw code should be a 5XX error
2797 }
2798 if (!parser.parse(tags_str.c_str(), tags_str.size(), 1)) {
2799 ldpp_dout(this,0 ) << "Invalid Tagging XML" << dendl;
2800 err_msg = "Invalid Tagging XML";
2801 return -EINVAL;
2802 }
2803
2804 RGWObjTagging_S3 tagging;
2805
2806 try {
2807 RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
2808 } catch (RGWXMLDecoder::err& err) {
2809 ldpp_dout(this, 5) << "Malformed tagging request: " << err << dendl;
2810 return -EINVAL;
2811 }
2812
2813 RGWObjTags obj_tags;
2814 int r = tagging.rebuild(obj_tags);
2815 if (r < 0)
2816 return r;
2817
2818 bufferlist tags_bl;
2819 obj_tags.encode(tags_bl);
2820 ldpp_dout(this, 20) << "Read " << obj_tags.count() << "tags" << dendl;
2821 attrs[RGW_ATTR_TAGS] = tags_bl;
2822 }
2823
2824
2825 return 0;
2826 }
2827
2828 int RGWPostObj_ObjStore_S3::get_policy(optional_yield y)
2829 {
2830 if (part_bl(parts, "policy", &s->auth.s3_postobj_creds.encoded_policy)) {
2831 bool aws4_auth = false;
2832
2833 /* x-amz-algorithm handling */
2834 using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
2835 if ((part_str(parts, "x-amz-algorithm", &s->auth.s3_postobj_creds.x_amz_algorithm)) &&
2836 (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR)) {
2837 ldpp_dout(this, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl;
2838 aws4_auth = true;
2839 } else {
2840 ldpp_dout(this, 0) << "Signature verification algorithm AWS v2" << dendl;
2841 }
2842
2843 // check that the signature matches the encoded policy
2844 if (aws4_auth) {
2845 /* AWS4 */
2846
2847 /* x-amz-credential handling */
2848 if (!part_str(parts, "x-amz-credential",
2849 &s->auth.s3_postobj_creds.x_amz_credential)) {
2850 ldpp_dout(this, 0) << "No S3 aws4 credential found!" << dendl;
2851 err_msg = "Missing aws4 credential";
2852 return -EINVAL;
2853 }
2854
2855 /* x-amz-signature handling */
2856 if (!part_str(parts, "x-amz-signature",
2857 &s->auth.s3_postobj_creds.signature)) {
2858 ldpp_dout(this, 0) << "No aws4 signature found!" << dendl;
2859 err_msg = "Missing aws4 signature";
2860 return -EINVAL;
2861 }
2862
2863 /* x-amz-date handling */
2864 std::string received_date_str;
2865 if (!part_str(parts, "x-amz-date", &received_date_str)) {
2866 ldpp_dout(this, 0) << "No aws4 date found!" << dendl;
2867 err_msg = "Missing aws4 date";
2868 return -EINVAL;
2869 }
2870 } else {
2871 /* AWS2 */
2872
2873 // check that the signature matches the encoded policy
2874 if (!part_str(parts, "AWSAccessKeyId",
2875 &s->auth.s3_postobj_creds.access_key)) {
2876 ldpp_dout(this, 0) << "No S3 aws2 access key found!" << dendl;
2877 err_msg = "Missing aws2 access key";
2878 return -EINVAL;
2879 }
2880
2881 if (!part_str(parts, "signature", &s->auth.s3_postobj_creds.signature)) {
2882 ldpp_dout(this, 0) << "No aws2 signature found!" << dendl;
2883 err_msg = "Missing aws2 signature";
2884 return -EINVAL;
2885 }
2886 }
2887
2888 if (part_str(parts, "x-amz-security-token", &s->auth.s3_postobj_creds.x_amz_security_token)) {
2889 if (s->auth.s3_postobj_creds.x_amz_security_token.size() == 0) {
2890 err_msg = "Invalid token";
2891 return -EINVAL;
2892 }
2893 }
2894
2895 /* FIXME: this is a makeshift solution. The browser upload authentication will be
2896 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
2897 * method. */
2898 const int ret = rgw::auth::Strategy::apply(this, auth_registry_ptr->get_s3_post(), s, y);
2899 if (ret != 0) {
2900 return -EACCES;
2901 } else {
2902 /* Populate the owner info. */
2903 s->owner.set_id(s->user->get_id());
2904 s->owner.set_name(s->user->get_display_name());
2905 ldpp_dout(this, 20) << "Successful Signature Verification!" << dendl;
2906 }
2907
2908 ceph::bufferlist decoded_policy;
2909 try {
2910 decoded_policy.decode_base64(s->auth.s3_postobj_creds.encoded_policy);
2911 } catch (buffer::error& err) {
2912 ldpp_dout(this, 0) << "failed to decode_base64 policy" << dendl;
2913 err_msg = "Could not decode policy";
2914 return -EINVAL;
2915 }
2916
2917 decoded_policy.append('\0'); // NULL terminate
2918 ldpp_dout(this, 20) << "POST policy: " << decoded_policy.c_str() << dendl;
2919
2920
2921 int r = post_policy.from_json(decoded_policy, err_msg);
2922 if (r < 0) {
2923 if (err_msg.empty()) {
2924 err_msg = "Failed to parse policy";
2925 }
2926 ldpp_dout(this, 0) << "failed to parse policy" << dendl;
2927 return -EINVAL;
2928 }
2929
2930 if (aws4_auth) {
2931 /* AWS4 */
2932 post_policy.set_var_checked("x-amz-signature");
2933 } else {
2934 /* AWS2 */
2935 post_policy.set_var_checked("AWSAccessKeyId");
2936 post_policy.set_var_checked("signature");
2937 }
2938 post_policy.set_var_checked("policy");
2939
2940 r = post_policy.check(&env, err_msg);
2941 if (r < 0) {
2942 if (err_msg.empty()) {
2943 err_msg = "Policy check failed";
2944 }
2945 ldpp_dout(this, 0) << "policy check failed" << dendl;
2946 return r;
2947 }
2948
2949 } else {
2950 ldpp_dout(this, 0) << "No attached policy found!" << dendl;
2951 }
2952
2953 string canned_acl;
2954 part_str(parts, "acl", &canned_acl);
2955
2956 RGWAccessControlPolicy_S3 s3policy(s->cct);
2957 ldpp_dout(this, 20) << "canned_acl=" << canned_acl << dendl;
2958 if (s3policy.create_canned(s->owner, s->bucket_owner, canned_acl) < 0) {
2959 err_msg = "Bad canned ACLs";
2960 return -EINVAL;
2961 }
2962
2963 policy = s3policy;
2964
2965 return 0;
2966 }
2967
2968 int RGWPostObj_ObjStore_S3::complete_get_params()
2969 {
2970 bool done;
2971 do {
2972 struct post_form_part part;
2973 int r = read_form_part_header(&part, done);
2974 if (r < 0) {
2975 return r;
2976 }
2977
2978 ceph::bufferlist part_data;
2979 bool boundary;
2980 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
2981 r = read_data(part.data, chunk_size, boundary, done);
2982 if (r < 0 || !boundary) {
2983 return -EINVAL;
2984 }
2985
2986 /* Just reading the data but not storing any results of that. */
2987 } while (!done);
2988
2989 return 0;
2990 }
2991
2992 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist& bl, bool& again)
2993 {
2994 bool boundary;
2995 bool done;
2996
2997 const uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
2998 int r = read_data(bl, chunk_size, boundary, done);
2999 if (r < 0) {
3000 return r;
3001 }
3002
3003 if (boundary) {
3004 if (!done) {
3005 /* Reached end of data, let's drain the rest of the params */
3006 r = complete_get_params();
3007 if (r < 0) {
3008 return r;
3009 }
3010 }
3011 }
3012
3013 again = !boundary;
3014 return bl.length();
3015 }
3016
3017 void RGWPostObj_ObjStore_S3::send_response()
3018 {
3019 if (op_ret == 0 && parts.count("success_action_redirect")) {
3020 string redirect;
3021
3022 part_str(parts, "success_action_redirect", &redirect);
3023
3024 string tenant;
3025 string bucket;
3026 string key;
3027 string etag_str = "\"";
3028
3029 etag_str.append(etag);
3030 etag_str.append("\"");
3031
3032 string etag_url;
3033
3034 url_encode(s->bucket_tenant, tenant); /* surely overkill, but cheap */
3035 url_encode(s->bucket_name, bucket);
3036 url_encode(s->object->get_name(), key);
3037 url_encode(etag_str, etag_url);
3038
3039 if (!s->bucket_tenant.empty()) {
3040 /*
3041 * What we really would like is to quaily the bucket name, so
3042 * that the client could simply copy it and paste into next request.
3043 * Unfortunately, in S3 we cannot know if the client will decide
3044 * to come through DNS, with "bucket.tenant" sytanx, or through
3045 * URL with "tenant\bucket" syntax. Therefore, we provide the
3046 * tenant separately.
3047 */
3048 redirect.append("?tenant=");
3049 redirect.append(tenant);
3050 redirect.append("&bucket=");
3051 redirect.append(bucket);
3052 } else {
3053 redirect.append("?bucket=");
3054 redirect.append(bucket);
3055 }
3056 redirect.append("&key=");
3057 redirect.append(key);
3058 redirect.append("&etag=");
3059 redirect.append(etag_url);
3060
3061 int r = check_utf8(redirect.c_str(), redirect.size());
3062 if (r < 0) {
3063 op_ret = r;
3064 goto done;
3065 }
3066 dump_redirect(s, redirect);
3067 op_ret = STATUS_REDIRECT;
3068 } else if (op_ret == 0 && parts.count("success_action_status")) {
3069 string status_string;
3070 uint32_t status_int;
3071
3072 part_str(parts, "success_action_status", &status_string);
3073
3074 int r = stringtoul(status_string, &status_int);
3075 if (r < 0) {
3076 op_ret = r;
3077 goto done;
3078 }
3079
3080 switch (status_int) {
3081 case 200:
3082 break;
3083 case 201:
3084 op_ret = STATUS_CREATED;
3085 break;
3086 default:
3087 op_ret = STATUS_NO_CONTENT;
3088 break;
3089 }
3090 } else if (! op_ret) {
3091 op_ret = STATUS_NO_CONTENT;
3092 }
3093
3094 done:
3095 if (op_ret == STATUS_CREATED) {
3096 for (auto &it : crypt_http_responses)
3097 dump_header(s, it.first, it.second);
3098 s->formatter->open_object_section("PostResponse");
3099 std::string base_uri = compute_domain_uri(s);
3100 if (!s->bucket_tenant.empty()){
3101 s->formatter->dump_format("Location", "%s/%s:%s/%s",
3102 base_uri.c_str(),
3103 url_encode(s->bucket_tenant).c_str(),
3104 url_encode(s->bucket_name).c_str(),
3105 url_encode(s->object->get_name()).c_str());
3106 s->formatter->dump_string("Tenant", s->bucket_tenant);
3107 } else {
3108 s->formatter->dump_format("Location", "%s/%s/%s",
3109 base_uri.c_str(),
3110 url_encode(s->bucket_name).c_str(),
3111 url_encode(s->object->get_name()).c_str());
3112 }
3113 s->formatter->dump_string("Bucket", s->bucket_name);
3114 s->formatter->dump_string("Key", s->object->get_name());
3115 s->formatter->dump_string("ETag", etag);
3116 s->formatter->close_section();
3117 }
3118 s->err.message = err_msg;
3119 set_req_state_err(s, op_ret);
3120 dump_errno(s);
3121 if (op_ret >= 0) {
3122 dump_content_length(s, s->formatter->get_len());
3123 }
3124 end_header(s, this);
3125 if (op_ret != STATUS_CREATED)
3126 return;
3127
3128 rgw_flush_formatter_and_reset(s, s->formatter);
3129 }
3130
3131 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
3132 std::unique_ptr<rgw::sal::DataProcessor> *filter,
3133 rgw::sal::DataProcessor *cb)
3134 {
3135 std::unique_ptr<BlockCrypt> block_crypt;
3136 int res = rgw_s3_prepare_encrypt(s, attrs, &parts, &block_crypt,
3137 crypt_http_responses);
3138 if (res == 0 && block_crypt != nullptr) {
3139 filter->reset(new RGWPutObj_BlockEncrypt(s, s->cct, cb, std::move(block_crypt)));
3140 }
3141 return res;
3142 }
3143
3144 int RGWDeleteObj_ObjStore_S3::get_params(optional_yield y)
3145 {
3146 const char *if_unmod = s->info.env->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
3147
3148 if (s->system_request) {
3149 s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "no-precondition-error", &no_precondition_error, false);
3150 }
3151
3152 if (if_unmod) {
3153 std::string if_unmod_decoded = url_decode(if_unmod);
3154 uint64_t epoch;
3155 uint64_t nsec;
3156 if (utime_t::parse_date(if_unmod_decoded, &epoch, &nsec) < 0) {
3157 ldpp_dout(this, 10) << "failed to parse time: " << if_unmod_decoded << dendl;
3158 return -EINVAL;
3159 }
3160 unmod_since = utime_t(epoch, nsec).to_real_time();
3161 }
3162
3163 const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
3164 if (bypass_gov_header) {
3165 std::string bypass_gov_decoded = url_decode(bypass_gov_header);
3166 bypass_governance_mode = boost::algorithm::iequals(bypass_gov_decoded, "true");
3167 }
3168
3169 return 0;
3170 }
3171
3172 void RGWDeleteObj_ObjStore_S3::send_response()
3173 {
3174 int r = op_ret;
3175 if (r == -ENOENT)
3176 r = 0;
3177 if (!r)
3178 r = STATUS_NO_CONTENT;
3179
3180 set_req_state_err(s, r);
3181 dump_errno(s);
3182 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
3183 if (delete_marker) {
3184 dump_header(s, "x-amz-delete-marker", "true");
3185 }
3186 end_header(s, this);
3187 }
3188
3189 int RGWCopyObj_ObjStore_S3::init_dest_policy()
3190 {
3191 RGWAccessControlPolicy_S3 s3policy(s->cct);
3192
3193 /* build a policy for the target object */
3194 int r = create_s3_policy(s, store, s3policy, s->owner);
3195 if (r < 0)
3196 return r;
3197
3198 dest_policy = s3policy;
3199
3200 return 0;
3201 }
3202
3203 int RGWCopyObj_ObjStore_S3::get_params(optional_yield y)
3204 {
3205 //handle object lock
3206 auto obj_lock_mode_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_MODE");
3207 auto obj_lock_date_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE");
3208 auto obj_legal_hold_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_LEGAL_HOLD");
3209 if (obj_lock_mode_str && obj_lock_date_str) {
3210 boost::optional<ceph::real_time> date = ceph::from_iso_8601(obj_lock_date_str);
3211 if (boost::none == date || ceph::real_clock::to_time_t(*date) <= ceph_clock_now()) {
3212 s->err.message = "invalid x-amz-object-lock-retain-until-date value";
3213 ldpp_dout(this,0) << s->err.message << dendl;
3214 return -EINVAL;
3215 }
3216 if (strcmp(obj_lock_mode_str, "GOVERNANCE") != 0 && strcmp(obj_lock_mode_str, "COMPLIANCE") != 0) {
3217 s->err.message = "invalid x-amz-object-lock-mode value";
3218 ldpp_dout(this,0) << s->err.message << dendl;
3219 return -EINVAL;
3220 }
3221 obj_retention = new RGWObjectRetention(obj_lock_mode_str, *date);
3222 } else if (obj_lock_mode_str || obj_lock_date_str) {
3223 s->err.message = "need both x-amz-object-lock-mode and x-amz-object-lock-retain-until-date ";
3224 ldpp_dout(this,0) << s->err.message << dendl;
3225 return -EINVAL;
3226 }
3227 if (obj_legal_hold_str) {
3228 if (strcmp(obj_legal_hold_str, "ON") != 0 && strcmp(obj_legal_hold_str, "OFF") != 0) {
3229 s->err.message = "invalid x-amz-object-lock-legal-hold value";
3230 ldpp_dout(this,0) << s->err.message << dendl;
3231 return -EINVAL;
3232 }
3233 obj_legal_hold = new RGWObjectLegalHold(obj_legal_hold_str);
3234 }
3235
3236 if_mod = s->info.env->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
3237 if_unmod = s->info.env->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
3238 if_match = s->info.env->get("HTTP_X_AMZ_COPY_IF_MATCH");
3239 if_nomatch = s->info.env->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
3240
3241 src_tenant_name = s->src_tenant_name;
3242 src_bucket_name = s->src_bucket_name;
3243 dest_tenant_name = s->bucket->get_tenant();
3244 dest_bucket_name = s->bucket->get_name();
3245 dest_obj_name = s->object->get_name();
3246
3247 if (s->system_request) {
3248 source_zone = s->info.args.get(RGW_SYS_PARAM_PREFIX "source-zone");
3249 s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "copy-if-newer", &copy_if_newer, false);
3250 }
3251
3252 copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
3253 auto tmp_md_d = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
3254 if (tmp_md_d) {
3255 if (strcasecmp(tmp_md_d, "COPY") == 0) {
3256 attrs_mod = rgw::sal::ATTRSMOD_NONE;
3257 } else if (strcasecmp(tmp_md_d, "REPLACE") == 0) {
3258 attrs_mod = rgw::sal::ATTRSMOD_REPLACE;
3259 } else if (!source_zone.empty()) {
3260 attrs_mod = rgw::sal::ATTRSMOD_NONE; // default for intra-zone_group copy
3261 } else {
3262 s->err.message = "Unknown metadata directive.";
3263 ldpp_dout(this, 0) << s->err.message << dendl;
3264 return -EINVAL;
3265 }
3266 md_directive = tmp_md_d;
3267 }
3268
3269 if (source_zone.empty() &&
3270 (dest_tenant_name.compare(src_tenant_name) == 0) &&
3271 (dest_bucket_name.compare(src_bucket_name) == 0) &&
3272 (dest_obj_name.compare(s->src_object->get_name()) == 0) &&
3273 s->src_object->get_instance().empty() &&
3274 (attrs_mod != rgw::sal::ATTRSMOD_REPLACE)) {
3275 need_to_check_storage_class = true;
3276 }
3277
3278 return 0;
3279 }
3280
3281 int RGWCopyObj_ObjStore_S3::check_storage_class(const rgw_placement_rule& src_placement)
3282 {
3283 if (src_placement == s->dest_placement) {
3284 /* can only copy object into itself if replacing attrs */
3285 s->err.message = "This copy request is illegal because it is trying to copy "
3286 "an object to itself without changing the object's metadata, "
3287 "storage class, website redirect location or encryption attributes.";
3288 ldpp_dout(this, 0) << s->err.message << dendl;
3289 return -ERR_INVALID_REQUEST;
3290 }
3291 return 0;
3292 }
3293
3294 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs)
3295 {
3296 if (! sent_header) {
3297 if (op_ret)
3298 set_req_state_err(s, op_ret);
3299 dump_errno(s);
3300
3301 // Explicitly use chunked transfer encoding so that we can stream the result
3302 // to the user without having to wait for the full length of it.
3303 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3304 dump_start(s);
3305 if (op_ret == 0) {
3306 s->formatter->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3);
3307 }
3308 sent_header = true;
3309 } else {
3310 /* Send progress field. Note that this diverge from the original S3
3311 * spec. We do this in order to keep connection alive.
3312 */
3313 s->formatter->dump_int("Progress", (uint64_t)ofs);
3314 }
3315 rgw_flush_formatter(s, s->formatter);
3316 }
3317
3318 void RGWCopyObj_ObjStore_S3::send_response()
3319 {
3320 if (!sent_header)
3321 send_partial_response(0);
3322
3323 if (op_ret == 0) {
3324 dump_time(s, "LastModified", mtime);
3325 if (!etag.empty()) {
3326 s->formatter->dump_string("ETag", std::move(etag));
3327 }
3328 s->formatter->close_section();
3329 rgw_flush_formatter_and_reset(s, s->formatter);
3330 }
3331 }
3332
3333 void RGWGetACLs_ObjStore_S3::send_response()
3334 {
3335 if (op_ret)
3336 set_req_state_err(s, op_ret);
3337 dump_errno(s);
3338 end_header(s, this, "application/xml");
3339 dump_start(s);
3340 rgw_flush_formatter(s, s->formatter);
3341 dump_body(s, acls);
3342 }
3343
3344 int RGWPutACLs_ObjStore_S3::get_params(optional_yield y)
3345 {
3346 int ret = RGWPutACLs_ObjStore::get_params(y);
3347 if (ret >= 0) {
3348 const int ret_auth = do_aws4_auth_completion();
3349 if (ret_auth < 0) {
3350 return ret_auth;
3351 }
3352 } else {
3353 /* a request body is not required an S3 PutACLs request--n.b.,
3354 * s->length is non-null iff a content length was parsed (the
3355 * ACP or canned ACL could be in any of 3 headers, don't worry
3356 * about that here) */
3357 if ((ret == -ERR_LENGTH_REQUIRED) &&
3358 !!(s->length)) {
3359 return 0;
3360 }
3361 }
3362 return ret;
3363 }
3364
3365 int RGWPutACLs_ObjStore_S3::get_policy_from_state(rgw::sal::Store* store,
3366 struct req_state *s,
3367 stringstream& ss)
3368 {
3369 RGWAccessControlPolicy_S3 s3policy(s->cct);
3370
3371 // bucket-* canned acls do not apply to bucket
3372 if (rgw::sal::Object::empty(s->object.get())) {
3373 if (s->canned_acl.find("bucket") != string::npos)
3374 s->canned_acl.clear();
3375 }
3376
3377 int r = create_s3_policy(s, store, s3policy, owner);
3378 if (r < 0)
3379 return r;
3380
3381 s3policy.to_xml(ss);
3382
3383 return 0;
3384 }
3385
3386 void RGWPutACLs_ObjStore_S3::send_response()
3387 {
3388 if (op_ret)
3389 set_req_state_err(s, op_ret);
3390 dump_errno(s);
3391 end_header(s, this, "application/xml");
3392 dump_start(s);
3393 }
3394
3395 void RGWGetLC_ObjStore_S3::execute(optional_yield y)
3396 {
3397 config.set_ctx(s->cct);
3398
3399 map<string, bufferlist>::iterator aiter = s->bucket_attrs.find(RGW_ATTR_LC);
3400 if (aiter == s->bucket_attrs.end()) {
3401 op_ret = -ENOENT;
3402 return;
3403 }
3404
3405 bufferlist::const_iterator iter{&aiter->second};
3406 try {
3407 config.decode(iter);
3408 } catch (const buffer::error& e) {
3409 ldpp_dout(this, 0) << __func__ << "decode life cycle config failed" << dendl;
3410 op_ret = -EIO;
3411 return;
3412 }
3413 }
3414
3415 void RGWGetLC_ObjStore_S3::send_response()
3416 {
3417 if (op_ret) {
3418 if (op_ret == -ENOENT) {
3419 set_req_state_err(s, ERR_NO_SUCH_LC);
3420 } else {
3421 set_req_state_err(s, op_ret);
3422 }
3423 }
3424 dump_errno(s);
3425 end_header(s, this, "application/xml");
3426 dump_start(s);
3427
3428 if (op_ret < 0)
3429 return;
3430
3431 encode_xml("LifecycleConfiguration", XMLNS_AWS_S3, config, s->formatter);
3432 rgw_flush_formatter_and_reset(s, s->formatter);
3433 }
3434
3435 void RGWPutLC_ObjStore_S3::send_response()
3436 {
3437 if (op_ret)
3438 set_req_state_err(s, op_ret);
3439 dump_errno(s);
3440 end_header(s, this, "application/xml");
3441 dump_start(s);
3442 }
3443
3444 void RGWDeleteLC_ObjStore_S3::send_response()
3445 {
3446 if (op_ret == 0)
3447 op_ret = STATUS_NO_CONTENT;
3448 if (op_ret) {
3449 set_req_state_err(s, op_ret);
3450 }
3451 dump_errno(s);
3452 end_header(s, this, "application/xml");
3453 dump_start(s);
3454 }
3455
3456 void RGWGetCORS_ObjStore_S3::send_response()
3457 {
3458 if (op_ret) {
3459 if (op_ret == -ENOENT)
3460 set_req_state_err(s, ERR_NO_SUCH_CORS_CONFIGURATION);
3461 else
3462 set_req_state_err(s, op_ret);
3463 }
3464 dump_errno(s);
3465 end_header(s, NULL, "application/xml");
3466 dump_start(s);
3467 if (! op_ret) {
3468 string cors;
3469 RGWCORSConfiguration_S3 *s3cors =
3470 static_cast<RGWCORSConfiguration_S3 *>(&bucket_cors);
3471 stringstream ss;
3472
3473 s3cors->to_xml(ss);
3474 cors = ss.str();
3475 dump_body(s, cors);
3476 }
3477 }
3478
3479 int RGWPutCORS_ObjStore_S3::get_params(optional_yield y)
3480 {
3481 RGWCORSXMLParser_S3 parser(this, s->cct);
3482 RGWCORSConfiguration_S3 *cors_config;
3483
3484 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
3485
3486 int r = 0;
3487 bufferlist data;
3488 std::tie(r, data) = read_all_input(s, max_size, false);
3489 if (r < 0) {
3490 return r;
3491 }
3492
3493 if (!parser.init()) {
3494 return -EINVAL;
3495 }
3496
3497 char* buf = data.c_str();
3498 if (!buf || !parser.parse(buf, data.length(), 1)) {
3499 return -ERR_MALFORMED_XML;
3500 }
3501 cors_config =
3502 static_cast<RGWCORSConfiguration_S3 *>(parser.find_first(
3503 "CORSConfiguration"));
3504 if (!cors_config) {
3505 return -ERR_MALFORMED_XML;
3506 }
3507
3508 #define CORS_RULES_MAX_NUM 100
3509 int max_num = s->cct->_conf->rgw_cors_rules_max_num;
3510 if (max_num < 0) {
3511 max_num = CORS_RULES_MAX_NUM;
3512 }
3513 int cors_rules_num = cors_config->get_rules().size();
3514 if (cors_rules_num > max_num) {
3515 ldpp_dout(this, 4) << "An cors config can have up to "
3516 << max_num
3517 << " rules, request cors rules num: "
3518 << cors_rules_num << dendl;
3519 op_ret = -ERR_INVALID_CORS_RULES_ERROR;
3520 s->err.message = "The number of CORS rules should not exceed allowed limit of "
3521 + std::to_string(max_num) + " rules.";
3522 return -ERR_INVALID_REQUEST;
3523 }
3524
3525 // forward bucket cors requests to meta master zone
3526 if (!store->is_meta_master()) {
3527 /* only need to keep this data around if we're not meta master */
3528 in_data.append(data);
3529 }
3530
3531 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
3532 ldpp_dout(this, 15) << "CORSConfiguration";
3533 cors_config->to_xml(*_dout);
3534 *_dout << dendl;
3535 }
3536
3537 cors_config->encode(cors_bl);
3538
3539 return 0;
3540 }
3541
3542 void RGWPutCORS_ObjStore_S3::send_response()
3543 {
3544 if (op_ret)
3545 set_req_state_err(s, op_ret);
3546 dump_errno(s);
3547 end_header(s, NULL, "application/xml");
3548 dump_start(s);
3549 }
3550
3551 void RGWDeleteCORS_ObjStore_S3::send_response()
3552 {
3553 int r = op_ret;
3554 if (!r || r == -ENOENT)
3555 r = STATUS_NO_CONTENT;
3556
3557 set_req_state_err(s, r);
3558 dump_errno(s);
3559 end_header(s, NULL);
3560 }
3561
3562 void RGWOptionsCORS_ObjStore_S3::send_response()
3563 {
3564 string hdrs, exp_hdrs;
3565 uint32_t max_age = CORS_MAX_AGE_INVALID;
3566 /*EACCES means, there is no CORS registered yet for the bucket
3567 *ENOENT means, there is no match of the Origin in the list of CORSRule
3568 */
3569 if (op_ret == -ENOENT)
3570 op_ret = -EACCES;
3571 if (op_ret < 0) {
3572 set_req_state_err(s, op_ret);
3573 dump_errno(s);
3574 end_header(s, NULL);
3575 return;
3576 }
3577 get_response_params(hdrs, exp_hdrs, &max_age);
3578
3579 dump_errno(s);
3580 dump_access_control(s, origin, req_meth, hdrs.c_str(), exp_hdrs.c_str(),
3581 max_age);
3582 end_header(s, NULL);
3583 }
3584
3585 void RGWPutBucketEncryption_ObjStore_S3::send_response()
3586 {
3587 if (op_ret) {
3588 set_req_state_err(s, op_ret);
3589 }
3590 dump_errno(s);
3591 end_header(s);
3592 }
3593
3594 void RGWGetBucketEncryption_ObjStore_S3::send_response()
3595 {
3596 if (op_ret) {
3597 if (op_ret == -ENOENT)
3598 set_req_state_err(s, ERR_NO_SUCH_BUCKET_ENCRYPTION_CONFIGURATION);
3599 else
3600 set_req_state_err(s, op_ret);
3601 }
3602
3603 dump_errno(s);
3604 end_header(s, this, "application/xml");
3605 dump_start(s);
3606
3607 if (!op_ret) {
3608 encode_xml("ServerSideEncryptionConfiguration", bucket_encryption_conf, s->formatter);
3609 rgw_flush_formatter_and_reset(s, s->formatter);
3610 }
3611 }
3612
3613 void RGWDeleteBucketEncryption_ObjStore_S3::send_response()
3614 {
3615 if (op_ret == 0) {
3616 op_ret = STATUS_NO_CONTENT;
3617 }
3618
3619 set_req_state_err(s, op_ret);
3620 dump_errno(s);
3621 end_header(s);
3622 }
3623
3624 void RGWGetRequestPayment_ObjStore_S3::send_response()
3625 {
3626 dump_errno(s);
3627 end_header(s, this, "application/xml");
3628 dump_start(s);
3629
3630 s->formatter->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3);
3631 const char *payer = requester_pays ? "Requester" : "BucketOwner";
3632 s->formatter->dump_string("Payer", payer);
3633 s->formatter->close_section();
3634 rgw_flush_formatter_and_reset(s, s->formatter);
3635 }
3636
3637 class RGWSetRequestPaymentParser : public RGWXMLParser
3638 {
3639 XMLObj *alloc_obj(const char *el) override {
3640 return new XMLObj;
3641 }
3642
3643 public:
3644 RGWSetRequestPaymentParser() {}
3645 ~RGWSetRequestPaymentParser() override {}
3646
3647 int get_request_payment_payer(bool *requester_pays) {
3648 XMLObj *config = find_first("RequestPaymentConfiguration");
3649 if (!config)
3650 return -EINVAL;
3651
3652 *requester_pays = false;
3653
3654 XMLObj *field = config->find_first("Payer");
3655 if (!field)
3656 return 0;
3657
3658 auto& s = field->get_data();
3659
3660 if (stringcasecmp(s, "Requester") == 0) {
3661 *requester_pays = true;
3662 } else if (stringcasecmp(s, "BucketOwner") != 0) {
3663 return -EINVAL;
3664 }
3665
3666 return 0;
3667 }
3668 };
3669
3670 int RGWSetRequestPayment_ObjStore_S3::get_params(optional_yield y)
3671 {
3672 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
3673
3674 int r = 0;
3675 std::tie(r, in_data) = read_all_input(s, max_size, false);
3676
3677 if (r < 0) {
3678 return r;
3679 }
3680
3681
3682 RGWSetRequestPaymentParser parser;
3683
3684 if (!parser.init()) {
3685 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
3686 return -EIO;
3687 }
3688
3689 char* buf = in_data.c_str();
3690 if (!parser.parse(buf, in_data.length(), 1)) {
3691 ldpp_dout(this, 10) << "failed to parse data: " << buf << dendl;
3692 return -EINVAL;
3693 }
3694
3695 return parser.get_request_payment_payer(&requester_pays);
3696 }
3697
3698 void RGWSetRequestPayment_ObjStore_S3::send_response()
3699 {
3700 if (op_ret)
3701 set_req_state_err(s, op_ret);
3702 dump_errno(s);
3703 end_header(s);
3704 }
3705
3706 int RGWInitMultipart_ObjStore_S3::get_params(optional_yield y)
3707 {
3708 RGWAccessControlPolicy_S3 s3policy(s->cct);
3709 op_ret = create_s3_policy(s, store, s3policy, s->owner);
3710 if (op_ret < 0)
3711 return op_ret;
3712
3713 policy = s3policy;
3714
3715 return 0;
3716 }
3717
3718 void RGWInitMultipart_ObjStore_S3::send_response()
3719 {
3720 if (op_ret)
3721 set_req_state_err(s, op_ret);
3722 dump_errno(s);
3723 for (auto &it : crypt_http_responses)
3724 dump_header(s, it.first, it.second);
3725 ceph::real_time abort_date;
3726 string rule_id;
3727 bool exist_multipart_abort = get_s3_multipart_abort_header(s, mtime, abort_date, rule_id);
3728 if (exist_multipart_abort) {
3729 dump_time_header(s, "x-amz-abort-date", abort_date);
3730 dump_header_if_nonempty(s, "x-amz-abort-rule-id", rule_id);
3731 }
3732 end_header(s, this, "application/xml");
3733 if (op_ret == 0) {
3734 dump_start(s);
3735 s->formatter->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3);
3736 if (!s->bucket_tenant.empty())
3737 s->formatter->dump_string("Tenant", s->bucket_tenant);
3738 s->formatter->dump_string("Bucket", s->bucket_name);
3739 s->formatter->dump_string("Key", s->object->get_name());
3740 s->formatter->dump_string("UploadId", upload_id);
3741 s->formatter->close_section();
3742 rgw_flush_formatter_and_reset(s, s->formatter);
3743 }
3744 }
3745
3746 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map<string, bufferlist>& attrs)
3747 {
3748 int res = 0;
3749 res = rgw_s3_prepare_encrypt(s, attrs, nullptr, nullptr, crypt_http_responses);
3750 return res;
3751 }
3752
3753 int RGWCompleteMultipart_ObjStore_S3::get_params(optional_yield y)
3754 {
3755 int ret = RGWCompleteMultipart_ObjStore::get_params(y);
3756 if (ret < 0) {
3757 return ret;
3758 }
3759
3760 map_qs_metadata(s);
3761
3762 return do_aws4_auth_completion();
3763 }
3764
3765 void RGWCompleteMultipart_ObjStore_S3::send_response()
3766 {
3767 if (op_ret)
3768 set_req_state_err(s, op_ret);
3769 dump_errno(s);
3770 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
3771 end_header(s, this, "application/xml");
3772 if (op_ret == 0) {
3773 dump_start(s);
3774 s->formatter->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3);
3775 std::string base_uri = compute_domain_uri(s);
3776 if (!s->bucket_tenant.empty()) {
3777 s->formatter->dump_format("Location", "%s/%s:%s/%s",
3778 base_uri.c_str(),
3779 s->bucket_tenant.c_str(),
3780 s->bucket_name.c_str(),
3781 s->object->get_name().c_str()
3782 );
3783 s->formatter->dump_string("Tenant", s->bucket_tenant);
3784 } else {
3785 s->formatter->dump_format("Location", "%s/%s/%s",
3786 base_uri.c_str(),
3787 s->bucket_name.c_str(),
3788 s->object->get_name().c_str()
3789 );
3790 }
3791 s->formatter->dump_string("Bucket", s->bucket_name);
3792 s->formatter->dump_string("Key", s->object->get_name());
3793 s->formatter->dump_string("ETag", etag);
3794 s->formatter->close_section();
3795 rgw_flush_formatter_and_reset(s, s->formatter);
3796 }
3797 }
3798
3799 void RGWAbortMultipart_ObjStore_S3::send_response()
3800 {
3801 int r = op_ret;
3802 if (!r)
3803 r = STATUS_NO_CONTENT;
3804
3805 set_req_state_err(s, r);
3806 dump_errno(s);
3807 end_header(s, this);
3808 }
3809
3810 void RGWListMultipart_ObjStore_S3::send_response()
3811 {
3812 if (op_ret)
3813 set_req_state_err(s, op_ret);
3814 dump_errno(s);
3815 // Explicitly use chunked transfer encoding so that we can stream the result
3816 // to the user without having to wait for the full length of it.
3817 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3818
3819 if (op_ret == 0) {
3820 dump_start(s);
3821 s->formatter->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3);
3822 map<uint32_t, std::unique_ptr<rgw::sal::MultipartPart>>::iterator iter;
3823 map<uint32_t, std::unique_ptr<rgw::sal::MultipartPart>>::reverse_iterator test_iter;
3824 int cur_max = 0;
3825
3826 iter = upload->get_parts().begin();
3827 test_iter = upload->get_parts().rbegin();
3828 if (test_iter != upload->get_parts().rend()) {
3829 cur_max = test_iter->first;
3830 }
3831 if (!s->bucket_tenant.empty())
3832 s->formatter->dump_string("Tenant", s->bucket_tenant);
3833 s->formatter->dump_string("Bucket", s->bucket_name);
3834 s->formatter->dump_string("Key", s->object->get_name());
3835 s->formatter->dump_string("UploadId", upload_id);
3836 s->formatter->dump_string("StorageClass", placement->get_storage_class());
3837 s->formatter->dump_int("PartNumberMarker", marker);
3838 s->formatter->dump_int("NextPartNumberMarker", cur_max);
3839 s->formatter->dump_int("MaxParts", max_parts);
3840 s->formatter->dump_string("IsTruncated", (truncated ? "true" : "false"));
3841
3842 ACLOwner& owner = policy.get_owner();
3843 dump_owner(s, owner.get_id(), owner.get_display_name());
3844
3845 for (; iter != upload->get_parts().end(); ++iter) {
3846 rgw::sal::MultipartPart* part = iter->second.get();
3847
3848 s->formatter->open_object_section("Part");
3849
3850 dump_time(s, "LastModified", part->get_mtime());
3851
3852 s->formatter->dump_unsigned("PartNumber", part->get_num());
3853 s->formatter->dump_format("ETag", "\"%s\"", part->get_etag().c_str());
3854 s->formatter->dump_unsigned("Size", part->get_size());
3855 s->formatter->close_section();
3856 }
3857 s->formatter->close_section();
3858 rgw_flush_formatter_and_reset(s, s->formatter);
3859 }
3860 }
3861
3862 void RGWListBucketMultiparts_ObjStore_S3::send_response()
3863 {
3864 if (op_ret < 0)
3865 set_req_state_err(s, op_ret);
3866 dump_errno(s);
3867
3868 // Explicitly use chunked transfer encoding so that we can stream the result
3869 // to the user without having to wait for the full length of it.
3870 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3871 dump_start(s);
3872 if (op_ret < 0)
3873 return;
3874
3875 s->formatter->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3);
3876 if (!s->bucket_tenant.empty())
3877 s->formatter->dump_string("Tenant", s->bucket_tenant);
3878 s->formatter->dump_string("Bucket", s->bucket_name);
3879 if (!prefix.empty())
3880 s->formatter->dump_string("Prefix", prefix);
3881 if (!marker_key.empty())
3882 s->formatter->dump_string("KeyMarker", marker_key);
3883 if (!marker_upload_id.empty())
3884 s->formatter->dump_string("UploadIdMarker", marker_upload_id);
3885 if (!next_marker_key.empty())
3886 s->formatter->dump_string("NextKeyMarker", next_marker_key);
3887 if (!next_marker_upload_id.empty())
3888 s->formatter->dump_string("NextUploadIdMarker", next_marker_upload_id);
3889 s->formatter->dump_int("MaxUploads", max_uploads);
3890 if (!delimiter.empty())
3891 s->formatter->dump_string("Delimiter", delimiter);
3892 s->formatter->dump_string("IsTruncated", (is_truncated ? "true" : "false"));
3893
3894 if (op_ret >= 0) {
3895 vector<std::unique_ptr<rgw::sal::MultipartUpload>>::iterator iter;
3896 for (iter = uploads.begin(); iter != uploads.end(); ++iter) {
3897 rgw::sal::MultipartUpload* upload = iter->get();
3898 s->formatter->open_array_section("Upload");
3899 if (encode_url) {
3900 s->formatter->dump_string("Key", url_encode(upload->get_key(), false));
3901 } else {
3902 s->formatter->dump_string("Key", upload->get_key());
3903 }
3904 s->formatter->dump_string("UploadId", upload->get_upload_id());
3905 const ACLOwner& owner = upload->get_owner();
3906 dump_owner(s, owner.get_id(), owner.get_display_name(), "Initiator");
3907 dump_owner(s, owner.get_id(), owner.get_display_name()); // Owner
3908 s->formatter->dump_string("StorageClass", "STANDARD");
3909 dump_time(s, "Initiated", upload->get_mtime());
3910 s->formatter->close_section();
3911 }
3912 if (!common_prefixes.empty()) {
3913 s->formatter->open_array_section("CommonPrefixes");
3914 for (const auto& kv : common_prefixes) {
3915 if (encode_url) {
3916 s->formatter->dump_string("Prefix", url_encode(kv.first, false));
3917 } else {
3918 s->formatter->dump_string("Prefix", kv.first);
3919 }
3920 }
3921 s->formatter->close_section();
3922 }
3923 }
3924 s->formatter->close_section();
3925 rgw_flush_formatter_and_reset(s, s->formatter);
3926 }
3927
3928 int RGWDeleteMultiObj_ObjStore_S3::get_params(optional_yield y)
3929 {
3930 int ret = RGWDeleteMultiObj_ObjStore::get_params(y);
3931 if (ret < 0) {
3932 return ret;
3933 }
3934
3935 const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
3936 if (bypass_gov_header) {
3937 std::string bypass_gov_decoded = url_decode(bypass_gov_header);
3938 bypass_governance_mode = boost::algorithm::iequals(bypass_gov_decoded, "true");
3939 }
3940
3941 return do_aws4_auth_completion();
3942 }
3943
3944 void RGWDeleteMultiObj_ObjStore_S3::send_status()
3945 {
3946 if (! status_dumped) {
3947 if (op_ret < 0)
3948 set_req_state_err(s, op_ret);
3949 dump_errno(s);
3950 status_dumped = true;
3951 }
3952 }
3953
3954 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
3955 {
3956
3957 if (!status_dumped) {
3958 send_status();
3959 }
3960
3961 dump_start(s);
3962 // Explicitly use chunked transfer encoding so that we can stream the result
3963 // to the user without having to wait for the full length of it.
3964 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3965 s->formatter->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3);
3966
3967 rgw_flush_formatter(s, s->formatter);
3968 }
3969
3970 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key& key,
3971 bool delete_marker,
3972 const string& marker_version_id, int ret)
3973 {
3974 if (!key.empty()) {
3975 if (ret == 0 && !quiet) {
3976 s->formatter->open_object_section("Deleted");
3977 s->formatter->dump_string("Key", key.name);
3978 if (!key.instance.empty()) {
3979 s->formatter->dump_string("VersionId", key.instance);
3980 }
3981 if (delete_marker) {
3982 s->formatter->dump_bool("DeleteMarker", true);
3983 s->formatter->dump_string("DeleteMarkerVersionId", marker_version_id);
3984 }
3985 s->formatter->close_section();
3986 } else if (ret < 0) {
3987 struct rgw_http_error r;
3988 int err_no;
3989
3990 s->formatter->open_object_section("Error");
3991
3992 err_no = -ret;
3993 rgw_get_errno_s3(&r, err_no);
3994
3995 s->formatter->dump_string("Key", key.name);
3996 s->formatter->dump_string("VersionId", key.instance);
3997 s->formatter->dump_string("Code", r.s3_code);
3998 s->formatter->dump_string("Message", r.s3_code);
3999 s->formatter->close_section();
4000 }
4001
4002 rgw_flush_formatter(s, s->formatter);
4003 }
4004 }
4005
4006 void RGWDeleteMultiObj_ObjStore_S3::end_response()
4007 {
4008
4009 s->formatter->close_section();
4010 rgw_flush_formatter_and_reset(s, s->formatter);
4011 }
4012
4013 void RGWGetObjLayout_ObjStore_S3::send_response()
4014 {
4015 if (op_ret)
4016 set_req_state_err(s, op_ret);
4017 dump_errno(s);
4018 end_header(s, this, "application/json");
4019
4020 JSONFormatter f;
4021
4022 if (op_ret < 0) {
4023 return;
4024 }
4025
4026 f.open_object_section("result");
4027 s->object->dump_obj_layout(this, s->yield, &f, s->obj_ctx);
4028 f.close_section();
4029 rgw_flush_formatter(s, &f);
4030 }
4031
4032 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params(optional_yield y)
4033 {
4034 auto iter = s->info.x_meta_map.find("x-amz-meta-search");
4035 if (iter == s->info.x_meta_map.end()) {
4036 s->err.message = "X-Rgw-Meta-Search header not provided";
4037 ldpp_dout(this, 5) << s->err.message << dendl;
4038 return -EINVAL;
4039 }
4040
4041 list<string> expressions;
4042 get_str_list(iter->second, ",", expressions);
4043
4044 for (auto& expression : expressions) {
4045 vector<string> args;
4046 get_str_vec(expression, ";", args);
4047
4048 if (args.empty()) {
4049 s->err.message = "invalid empty expression";
4050 ldpp_dout(this, 5) << s->err.message << dendl;
4051 return -EINVAL;
4052 }
4053 if (args.size() > 2) {
4054 s->err.message = string("invalid expression: ") + expression;
4055 ldpp_dout(this, 5) << s->err.message << dendl;
4056 return -EINVAL;
4057 }
4058
4059 string key = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[0]));
4060 string val;
4061 if (args.size() > 1) {
4062 val = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[1]));
4063 }
4064
4065 if (!boost::algorithm::starts_with(key, RGW_AMZ_META_PREFIX)) {
4066 s->err.message = string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX "' : ") + expression;
4067 ldpp_dout(this, 5) << s->err.message << dendl;
4068 return -EINVAL;
4069 }
4070
4071 key = key.substr(sizeof(RGW_AMZ_META_PREFIX) - 1);
4072
4073 ESEntityTypeMap::EntityType entity_type;
4074
4075 if (val.empty() || val == "str" || val == "string") {
4076 entity_type = ESEntityTypeMap::ES_ENTITY_STR;
4077 } else if (val == "int" || val == "integer") {
4078 entity_type = ESEntityTypeMap::ES_ENTITY_INT;
4079 } else if (val == "date" || val == "datetime") {
4080 entity_type = ESEntityTypeMap::ES_ENTITY_DATE;
4081 } else {
4082 s->err.message = string("invalid entity type: ") + val;
4083 ldpp_dout(this, 5) << s->err.message << dendl;
4084 return -EINVAL;
4085 }
4086
4087 mdsearch_config[key] = entity_type;
4088 }
4089
4090 return 0;
4091 }
4092
4093 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
4094 {
4095 if (op_ret)
4096 set_req_state_err(s, op_ret);
4097 dump_errno(s);
4098 end_header(s, this);
4099 }
4100
4101 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
4102 {
4103 if (op_ret)
4104 set_req_state_err(s, op_ret);
4105 dump_errno(s);
4106 end_header(s, NULL, "application/xml");
4107
4108 Formatter *f = s->formatter;
4109 f->open_array_section("GetBucketMetaSearchResult");
4110 for (auto& e : s->bucket->get_info().mdsearch_config) {
4111 f->open_object_section("Entry");
4112 string k = string("x-amz-meta-") + e.first;
4113 f->dump_string("Key", k.c_str());
4114 const char *type;
4115 switch (e.second) {
4116 case ESEntityTypeMap::ES_ENTITY_INT:
4117 type = "int";
4118 break;
4119 case ESEntityTypeMap::ES_ENTITY_DATE:
4120 type = "date";
4121 break;
4122 default:
4123 type = "str";
4124 }
4125 f->dump_string("Type", type);
4126 f->close_section();
4127 }
4128 f->close_section();
4129 rgw_flush_formatter(s, f);
4130 }
4131
4132 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
4133 {
4134 if (op_ret)
4135 set_req_state_err(s, op_ret);
4136 dump_errno(s);
4137 end_header(s, this);
4138 }
4139
4140 void RGWPutBucketObjectLock_ObjStore_S3::send_response()
4141 {
4142 if (op_ret) {
4143 set_req_state_err(s, op_ret);
4144 }
4145 dump_errno(s);
4146 end_header(s);
4147 }
4148
4149 void RGWGetBucketObjectLock_ObjStore_S3::send_response()
4150 {
4151 if (op_ret) {
4152 set_req_state_err(s, op_ret);
4153 }
4154 dump_errno(s);
4155 end_header(s, this, "application/xml");
4156 dump_start(s);
4157
4158 if (op_ret) {
4159 return;
4160 }
4161 encode_xml("ObjectLockConfiguration", s->bucket->get_info().obj_lock, s->formatter);
4162 rgw_flush_formatter_and_reset(s, s->formatter);
4163 }
4164
4165
4166 int RGWPutObjRetention_ObjStore_S3::get_params(optional_yield y)
4167 {
4168 const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
4169 if (bypass_gov_header) {
4170 std::string bypass_gov_decoded = url_decode(bypass_gov_header);
4171 bypass_governance_mode = boost::algorithm::iequals(bypass_gov_decoded, "true");
4172 }
4173
4174 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
4175 std::tie(op_ret, data) = read_all_input(s, max_size, false);
4176 return op_ret;
4177 }
4178
4179 void RGWPutObjRetention_ObjStore_S3::send_response()
4180 {
4181 if (op_ret) {
4182 set_req_state_err(s, op_ret);
4183 }
4184 dump_errno(s);
4185 end_header(s);
4186 }
4187
4188 void RGWGetObjRetention_ObjStore_S3::send_response()
4189 {
4190 if (op_ret) {
4191 set_req_state_err(s, op_ret);
4192 }
4193 dump_errno(s);
4194 end_header(s, this, "application/xml");
4195 dump_start(s);
4196
4197 if (op_ret) {
4198 return;
4199 }
4200 encode_xml("Retention", obj_retention, s->formatter);
4201 rgw_flush_formatter_and_reset(s, s->formatter);
4202 }
4203
4204 void RGWPutObjLegalHold_ObjStore_S3::send_response()
4205 {
4206 if (op_ret) {
4207 set_req_state_err(s, op_ret);
4208 }
4209 dump_errno(s);
4210 end_header(s);
4211 }
4212
4213 void RGWGetObjLegalHold_ObjStore_S3::send_response()
4214 {
4215 if (op_ret) {
4216 set_req_state_err(s, op_ret);
4217 }
4218 dump_errno(s);
4219 end_header(s, this, "application/xml");
4220 dump_start(s);
4221
4222 if (op_ret) {
4223 return;
4224 }
4225 encode_xml("LegalHold", obj_legal_hold, s->formatter);
4226 rgw_flush_formatter_and_reset(s, s->formatter);
4227 }
4228
4229 void RGWGetBucketPolicyStatus_ObjStore_S3::send_response()
4230 {
4231 if (op_ret) {
4232 set_req_state_err(s, op_ret);
4233 }
4234 dump_errno(s);
4235 end_header(s, this, "application/xml");
4236 dump_start(s);
4237
4238 s->formatter->open_object_section_in_ns("PolicyStatus", XMLNS_AWS_S3);
4239 // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETPolicyStatus.html
4240 // mentions TRUE and FALSE, but boto/aws official clients seem to want lower
4241 // case which is returned by AWS as well; so let's be bug to bug compatible
4242 // with the API
4243 s->formatter->dump_bool("IsPublic", isPublic);
4244 s->formatter->close_section();
4245 rgw_flush_formatter_and_reset(s, s->formatter);
4246
4247 }
4248
4249 void RGWPutBucketPublicAccessBlock_ObjStore_S3::send_response()
4250 {
4251 if (op_ret) {
4252 set_req_state_err(s, op_ret);
4253 }
4254 dump_errno(s);
4255 end_header(s);
4256 }
4257
4258 void RGWGetBucketPublicAccessBlock_ObjStore_S3::send_response()
4259 {
4260 if (op_ret) {
4261 set_req_state_err(s, op_ret);
4262 }
4263 dump_errno(s);
4264 end_header(s, this, "application/xml");
4265 dump_start(s);
4266
4267 access_conf.dump_xml(s->formatter);
4268 rgw_flush_formatter_and_reset(s, s->formatter);
4269 }
4270
4271 RGWOp *RGWHandler_REST_Service_S3::op_get()
4272 {
4273 if (is_usage_op()) {
4274 return new RGWGetUsage_ObjStore_S3;
4275 } else {
4276 return new RGWListBuckets_ObjStore_S3;
4277 }
4278 }
4279
4280 RGWOp *RGWHandler_REST_Service_S3::op_head()
4281 {
4282 return new RGWListBuckets_ObjStore_S3;
4283 }
4284
4285 RGWOp *RGWHandler_REST_Service_S3::op_post()
4286 {
4287 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
4288
4289 int ret;
4290 bufferlist data;
4291 std::tie(ret, data) = rgw_rest_read_all_input(s, max_size, false);
4292 if (ret < 0) {
4293 return nullptr;
4294 }
4295
4296 const auto post_body = data.to_str();
4297
4298 if (isSTSEnabled) {
4299 RGWHandler_REST_STS sts_handler(auth_registry, post_body);
4300 sts_handler.init(store, s, s->cio);
4301 auto op = sts_handler.get_op();
4302 if (op) {
4303 return op;
4304 }
4305 }
4306
4307 if (isIAMEnabled) {
4308 RGWHandler_REST_IAM iam_handler(auth_registry, post_body);
4309 iam_handler.init(store, s, s->cio);
4310 auto op = iam_handler.get_op();
4311 if (op) {
4312 return op;
4313 }
4314 }
4315
4316 if (isPSEnabled) {
4317 RGWHandler_REST_PSTopic_AWS topic_handler(auth_registry, post_body);
4318 topic_handler.init(store, s, s->cio);
4319 auto op = topic_handler.get_op();
4320 if (op) {
4321 return op;
4322 }
4323 }
4324
4325 return nullptr;
4326 }
4327
4328 RGWOp *RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data) const
4329 {
4330 // Non-website mode
4331 if (get_data) {
4332 int list_type = 1;
4333 s->info.args.get_int("list-type", &list_type, 1);
4334 switch (list_type) {
4335 case 1:
4336 return new RGWListBucket_ObjStore_S3;
4337 case 2:
4338 return new RGWListBucket_ObjStore_S3v2;
4339 default:
4340 ldpp_dout(s, 5) << __func__ << ": unsupported list-type " << list_type << dendl;
4341 return new RGWListBucket_ObjStore_S3;
4342 }
4343 } else {
4344 return new RGWStatBucket_ObjStore_S3;
4345 }
4346 }
4347
4348 RGWOp *RGWHandler_REST_Bucket_S3::op_get()
4349 {
4350 if (s->info.args.sub_resource_exists("encryption"))
4351 return nullptr;
4352
4353 if (s->info.args.sub_resource_exists("logging"))
4354 return new RGWGetBucketLogging_ObjStore_S3;
4355
4356 if (s->info.args.sub_resource_exists("location"))
4357 return new RGWGetBucketLocation_ObjStore_S3;
4358
4359 if (s->info.args.sub_resource_exists("versioning"))
4360 return new RGWGetBucketVersioning_ObjStore_S3;
4361
4362 if (s->info.args.sub_resource_exists("website")) {
4363 if (!s->cct->_conf->rgw_enable_static_website) {
4364 return NULL;
4365 }
4366 return new RGWGetBucketWebsite_ObjStore_S3;
4367 }
4368
4369 if (s->info.args.exists("mdsearch")) {
4370 return new RGWGetBucketMetaSearch_ObjStore_S3;
4371 }
4372
4373 if (is_acl_op()) {
4374 return new RGWGetACLs_ObjStore_S3;
4375 } else if (is_cors_op()) {
4376 return new RGWGetCORS_ObjStore_S3;
4377 } else if (is_request_payment_op()) {
4378 return new RGWGetRequestPayment_ObjStore_S3;
4379 } else if (s->info.args.exists("uploads")) {
4380 return new RGWListBucketMultiparts_ObjStore_S3;
4381 } else if(is_lc_op()) {
4382 return new RGWGetLC_ObjStore_S3;
4383 } else if(is_policy_op()) {
4384 return new RGWGetBucketPolicy;
4385 } else if (is_tagging_op()) {
4386 return new RGWGetBucketTags_ObjStore_S3;
4387 } else if (is_object_lock_op()) {
4388 return new RGWGetBucketObjectLock_ObjStore_S3;
4389 } else if (is_notification_op()) {
4390 return RGWHandler_REST_PSNotifs_S3::create_get_op();
4391 } else if (is_replication_op()) {
4392 return new RGWGetBucketReplication_ObjStore_S3;
4393 } else if (is_policy_status_op()) {
4394 return new RGWGetBucketPolicyStatus_ObjStore_S3;
4395 } else if (is_block_public_access_op()) {
4396 return new RGWGetBucketPublicAccessBlock_ObjStore_S3;
4397 } else if (is_bucket_encryption_op()) {
4398 return new RGWGetBucketEncryption_ObjStore_S3;
4399 }
4400 return get_obj_op(true);
4401 }
4402
4403 RGWOp *RGWHandler_REST_Bucket_S3::op_head()
4404 {
4405 if (is_acl_op()) {
4406 return new RGWGetACLs_ObjStore_S3;
4407 } else if (s->info.args.exists("uploads")) {
4408 return new RGWListBucketMultiparts_ObjStore_S3;
4409 }
4410 return get_obj_op(false);
4411 }
4412
4413 RGWOp *RGWHandler_REST_Bucket_S3::op_put()
4414 {
4415 if (s->info.args.sub_resource_exists("logging") ||
4416 s->info.args.sub_resource_exists("encryption"))
4417 return nullptr;
4418 if (s->info.args.sub_resource_exists("versioning"))
4419 return new RGWSetBucketVersioning_ObjStore_S3;
4420 if (s->info.args.sub_resource_exists("website")) {
4421 if (!s->cct->_conf->rgw_enable_static_website) {
4422 return NULL;
4423 }
4424 return new RGWSetBucketWebsite_ObjStore_S3;
4425 }
4426 if (is_tagging_op()) {
4427 return new RGWPutBucketTags_ObjStore_S3;
4428 } else if (is_acl_op()) {
4429 return new RGWPutACLs_ObjStore_S3;
4430 } else if (is_cors_op()) {
4431 return new RGWPutCORS_ObjStore_S3;
4432 } else if (is_request_payment_op()) {
4433 return new RGWSetRequestPayment_ObjStore_S3;
4434 } else if(is_lc_op()) {
4435 return new RGWPutLC_ObjStore_S3;
4436 } else if(is_policy_op()) {
4437 return new RGWPutBucketPolicy;
4438 } else if (is_object_lock_op()) {
4439 return new RGWPutBucketObjectLock_ObjStore_S3;
4440 } else if (is_notification_op()) {
4441 return RGWHandler_REST_PSNotifs_S3::create_put_op();
4442 } else if (is_replication_op()) {
4443 auto sync_policy_handler = static_cast<rgw::sal::RadosStore*>(store)->svc()->zone->get_sync_policy_handler(nullopt);
4444 if (!sync_policy_handler ||
4445 sync_policy_handler->is_legacy_config()) {
4446 return nullptr;
4447 }
4448
4449 return new RGWPutBucketReplication_ObjStore_S3;
4450 } else if (is_block_public_access_op()) {
4451 return new RGWPutBucketPublicAccessBlock_ObjStore_S3;
4452 } else if (is_bucket_encryption_op()) {
4453 return new RGWPutBucketEncryption_ObjStore_S3;
4454 }
4455 return new RGWCreateBucket_ObjStore_S3;
4456 }
4457
4458 RGWOp *RGWHandler_REST_Bucket_S3::op_delete()
4459 {
4460 if (s->info.args.sub_resource_exists("logging") ||
4461 s->info.args.sub_resource_exists("encryption"))
4462 return nullptr;
4463
4464 if (is_tagging_op()) {
4465 return new RGWDeleteBucketTags_ObjStore_S3;
4466 } else if (is_cors_op()) {
4467 return new RGWDeleteCORS_ObjStore_S3;
4468 } else if(is_lc_op()) {
4469 return new RGWDeleteLC_ObjStore_S3;
4470 } else if(is_policy_op()) {
4471 return new RGWDeleteBucketPolicy;
4472 } else if (is_notification_op()) {
4473 return RGWHandler_REST_PSNotifs_S3::create_delete_op();
4474 } else if (is_replication_op()) {
4475 return new RGWDeleteBucketReplication_ObjStore_S3;
4476 } else if (is_block_public_access_op()) {
4477 return new RGWDeleteBucketPublicAccessBlock;
4478 } else if (is_bucket_encryption_op()) {
4479 return new RGWDeleteBucketEncryption_ObjStore_S3;
4480 }
4481
4482 if (s->info.args.sub_resource_exists("website")) {
4483 if (!s->cct->_conf->rgw_enable_static_website) {
4484 return NULL;
4485 }
4486 return new RGWDeleteBucketWebsite_ObjStore_S3;
4487 }
4488
4489 if (s->info.args.exists("mdsearch")) {
4490 return new RGWDelBucketMetaSearch_ObjStore_S3;
4491 }
4492
4493 return new RGWDeleteBucket_ObjStore_S3;
4494 }
4495
4496 RGWOp *RGWHandler_REST_Bucket_S3::op_post()
4497 {
4498 if (s->info.args.exists("delete")) {
4499 return new RGWDeleteMultiObj_ObjStore_S3;
4500 }
4501
4502 if (s->info.args.exists("mdsearch")) {
4503 return new RGWConfigBucketMetaSearch_ObjStore_S3;
4504 }
4505
4506 return new RGWPostObj_ObjStore_S3;
4507 }
4508
4509 RGWOp *RGWHandler_REST_Bucket_S3::op_options()
4510 {
4511 return new RGWOptionsCORS_ObjStore_S3;
4512 }
4513
4514 RGWOp *RGWHandler_REST_Obj_S3::get_obj_op(bool get_data)
4515 {
4516 RGWGetObj_ObjStore_S3 *get_obj_op = new RGWGetObj_ObjStore_S3;
4517 get_obj_op->set_get_data(get_data);
4518 return get_obj_op;
4519 }
4520
4521 RGWOp *RGWHandler_REST_Obj_S3::op_get()
4522 {
4523 if (is_acl_op()) {
4524 return new RGWGetACLs_ObjStore_S3;
4525 } else if (s->info.args.exists("uploadId")) {
4526 return new RGWListMultipart_ObjStore_S3;
4527 } else if (s->info.args.exists("layout")) {
4528 return new RGWGetObjLayout_ObjStore_S3;
4529 } else if (is_tagging_op()) {
4530 return new RGWGetObjTags_ObjStore_S3;
4531 } else if (is_obj_retention_op()) {
4532 return new RGWGetObjRetention_ObjStore_S3;
4533 } else if (is_obj_legal_hold_op()) {
4534 return new RGWGetObjLegalHold_ObjStore_S3;
4535 }
4536 return get_obj_op(true);
4537 }
4538
4539 RGWOp *RGWHandler_REST_Obj_S3::op_head()
4540 {
4541 if (is_acl_op()) {
4542 return new RGWGetACLs_ObjStore_S3;
4543 } else if (s->info.args.exists("uploadId")) {
4544 return new RGWListMultipart_ObjStore_S3;
4545 }
4546 return get_obj_op(false);
4547 }
4548
4549 RGWOp *RGWHandler_REST_Obj_S3::op_put()
4550 {
4551 if (is_acl_op()) {
4552 return new RGWPutACLs_ObjStore_S3;
4553 } else if (is_tagging_op()) {
4554 return new RGWPutObjTags_ObjStore_S3;
4555 } else if (is_obj_retention_op()) {
4556 return new RGWPutObjRetention_ObjStore_S3;
4557 } else if (is_obj_legal_hold_op()) {
4558 return new RGWPutObjLegalHold_ObjStore_S3;
4559 }
4560
4561 if (s->init_state.src_bucket.empty())
4562 return new RGWPutObj_ObjStore_S3;
4563 else
4564 return new RGWCopyObj_ObjStore_S3;
4565 }
4566
4567 RGWOp *RGWHandler_REST_Obj_S3::op_delete()
4568 {
4569 if (is_tagging_op()) {
4570 return new RGWDeleteObjTags_ObjStore_S3;
4571 }
4572 string upload_id = s->info.args.get("uploadId");
4573
4574 if (upload_id.empty())
4575 return new RGWDeleteObj_ObjStore_S3;
4576 else
4577 return new RGWAbortMultipart_ObjStore_S3;
4578 }
4579
4580 RGWOp *RGWHandler_REST_Obj_S3::op_post()
4581 {
4582 if (s->info.args.exists("uploadId"))
4583 return new RGWCompleteMultipart_ObjStore_S3;
4584
4585 if (s->info.args.exists("uploads"))
4586 return new RGWInitMultipart_ObjStore_S3;
4587
4588 if (is_select_op())
4589 return rgw::s3select::create_s3select_op();
4590
4591 return new RGWPostObj_ObjStore_S3;
4592 }
4593
4594 RGWOp *RGWHandler_REST_Obj_S3::op_options()
4595 {
4596 return new RGWOptionsCORS_ObjStore_S3;
4597 }
4598
4599 int RGWHandler_REST_S3::init_from_header(rgw::sal::Store* store,
4600 struct req_state* s,
4601 int default_formatter,
4602 bool configurable_format)
4603 {
4604 string req;
4605 string first;
4606
4607 const char *req_name = s->relative_uri.c_str();
4608 const char *p;
4609
4610 if (*req_name == '?') {
4611 p = req_name;
4612 } else {
4613 p = s->info.request_params.c_str();
4614 }
4615
4616 s->info.args.set(p);
4617 s->info.args.parse(s);
4618
4619 /* must be called after the args parsing */
4620 int ret = allocate_formatter(s, default_formatter, configurable_format);
4621 if (ret < 0)
4622 return ret;
4623
4624 if (*req_name != '/')
4625 return 0;
4626
4627 req_name++;
4628
4629 if (!*req_name)
4630 return 0;
4631
4632 req = req_name;
4633 int pos = req.find('/');
4634 if (pos >= 0) {
4635 first = req.substr(0, pos);
4636 } else {
4637 first = req;
4638 }
4639
4640 /*
4641 * XXX The intent of the check for empty is apparently to let the bucket
4642 * name from DNS to be set ahead. However, we currently take the DNS
4643 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
4644 * So, this check is meaningless.
4645 *
4646 * Rather than dropping this, the code needs to be changed into putting
4647 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
4648 * into req_status.bucket_name directly.
4649 */
4650 if (s->init_state.url_bucket.empty()) {
4651 // Save bucket to tide us over until token is parsed.
4652 s->init_state.url_bucket = first;
4653 string encoded_obj_str;
4654 if (pos >= 0) {
4655 encoded_obj_str = req.substr(pos+1);
4656 }
4657
4658 /* dang: s->bucket is never set here, since it's created with permissions.
4659 * These calls will always create an object with no bucket. */
4660 if (!encoded_obj_str.empty()) {
4661 if (s->bucket) {
4662 s->object = s->bucket->get_object(rgw_obj_key(encoded_obj_str, s->info.args.get("versionId")));
4663 } else {
4664 s->object = store->get_object(rgw_obj_key(encoded_obj_str, s->info.args.get("versionId")));
4665 }
4666 }
4667 } else {
4668 if (s->bucket) {
4669 s->object = s->bucket->get_object(rgw_obj_key(req_name, s->info.args.get("versionId")));
4670 } else {
4671 s->object = store->get_object(rgw_obj_key(req_name, s->info.args.get("versionId")));
4672 }
4673 }
4674 return 0;
4675 }
4676
4677 static int verify_mfa(rgw::sal::Store* store, RGWUserInfo *user,
4678 const string& mfa_str, bool *verified, const DoutPrefixProvider *dpp, optional_yield y)
4679 {
4680 vector<string> params;
4681 get_str_vec(mfa_str, " ", params);
4682
4683 if (params.size() != 2) {
4684 ldpp_dout(dpp, 5) << "NOTICE: invalid mfa string provided: " << mfa_str << dendl;
4685 return -EINVAL;
4686 }
4687
4688 string& serial = params[0];
4689 string& pin = params[1];
4690
4691 auto i = user->mfa_ids.find(serial);
4692 if (i == user->mfa_ids.end()) {
4693 ldpp_dout(dpp, 5) << "NOTICE: user does not have mfa device with serial=" << serial << dendl;
4694 return -EACCES;
4695 }
4696
4697 int ret = static_cast<rgw::sal::RadosStore*>(store)->svc()->cls->mfa.check_mfa(dpp, user->user_id, serial, pin, y);
4698 if (ret < 0) {
4699 ldpp_dout(dpp, 20) << "NOTICE: failed to check MFA, serial=" << serial << dendl;
4700 return -EACCES;
4701 }
4702
4703 *verified = true;
4704
4705 return 0;
4706 }
4707
4708 int RGWHandler_REST_S3::postauth_init(optional_yield y)
4709 {
4710 struct req_init_state *t = &s->init_state;
4711
4712 rgw_parse_url_bucket(t->url_bucket, s->user->get_tenant(),
4713 s->bucket_tenant, s->bucket_name);
4714
4715 if (s->auth.identity->get_identity_type() == TYPE_ROLE) {
4716 s->bucket_tenant = s->auth.identity->get_role_tenant();
4717 }
4718
4719 ldpp_dout(s, 10) << "s->object=" << s->object
4720 << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
4721
4722 int ret;
4723 ret = rgw_validate_tenant_name(s->bucket_tenant);
4724 if (ret)
4725 return ret;
4726 if (!s->bucket_name.empty() && !rgw::sal::Object::empty(s->object.get())) {
4727 ret = validate_object_name(s->object->get_name());
4728 if (ret)
4729 return ret;
4730 }
4731
4732 if (!t->src_bucket.empty()) {
4733 string auth_tenant;
4734 if (s->auth.identity->get_identity_type() == TYPE_ROLE) {
4735 auth_tenant = s->auth.identity->get_role_tenant();
4736 } else {
4737 auth_tenant = s->user->get_tenant();
4738 }
4739 rgw_parse_url_bucket(t->src_bucket, auth_tenant,
4740 s->src_tenant_name, s->src_bucket_name);
4741 ret = rgw_validate_tenant_name(s->src_tenant_name);
4742 if (ret)
4743 return ret;
4744 }
4745
4746 const char *mfa = s->info.env->get("HTTP_X_AMZ_MFA");
4747 if (mfa) {
4748 ret = verify_mfa(store, &s->user->get_info(), string(mfa), &s->mfa_verified, s, y);
4749 }
4750
4751 return 0;
4752 }
4753
4754 int RGWHandler_REST_S3::init(rgw::sal::Store* store, struct req_state *s,
4755 rgw::io::BasicClient *cio)
4756 {
4757 int ret;
4758
4759 s->dialect = "s3";
4760
4761 ret = rgw_validate_tenant_name(s->bucket_tenant);
4762 if (ret)
4763 return ret;
4764 if (!s->bucket_name.empty()) {
4765 ret = validate_object_name(s->object->get_name());
4766 if (ret)
4767 return ret;
4768 }
4769
4770 const char *cacl = s->info.env->get("HTTP_X_AMZ_ACL");
4771 if (cacl)
4772 s->canned_acl = cacl;
4773
4774 s->has_acl_header = s->info.env->exists_prefix("HTTP_X_AMZ_GRANT");
4775
4776 const char *copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
4777 if (copy_source &&
4778 (! s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) &&
4779 (! s->info.args.exists("uploadId"))) {
4780 rgw_obj_key key;
4781
4782 ret = RGWCopyObj::parse_copy_location(copy_source,
4783 s->init_state.src_bucket,
4784 key,
4785 s);
4786 if (!ret) {
4787 ldpp_dout(s, 0) << "failed to parse copy location" << dendl;
4788 return -EINVAL; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
4789 }
4790 s->src_object = store->get_object(key);
4791 }
4792
4793 const char *sc = s->info.env->get("HTTP_X_AMZ_STORAGE_CLASS");
4794 if (sc) {
4795 s->info.storage_class = sc;
4796 }
4797
4798 return RGWHandler_REST::init(store, s, cio);
4799 }
4800
4801 int RGWHandler_REST_S3::authorize(const DoutPrefixProvider *dpp, optional_yield y)
4802 {
4803 if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
4804 return RGW_Auth_STS::authorize(dpp, store, auth_registry, s, y);
4805 }
4806 return RGW_Auth_S3::authorize(dpp, store, auth_registry, s, y);
4807 }
4808
4809 enum class AwsVersion {
4810 UNKNOWN,
4811 V2,
4812 V4
4813 };
4814
4815 enum class AwsRoute {
4816 UNKNOWN,
4817 QUERY_STRING,
4818 HEADERS
4819 };
4820
4821 static inline std::pair<AwsVersion, AwsRoute>
4822 discover_aws_flavour(const req_info& info)
4823 {
4824 using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
4825
4826 AwsVersion version = AwsVersion::UNKNOWN;
4827 AwsRoute route = AwsRoute::UNKNOWN;
4828
4829 const char* http_auth = info.env->get("HTTP_AUTHORIZATION");
4830 if (http_auth && http_auth[0]) {
4831 /* Authorization in Header */
4832 route = AwsRoute::HEADERS;
4833
4834 if (!strncmp(http_auth, AWS4_HMAC_SHA256_STR,
4835 strlen(AWS4_HMAC_SHA256_STR))) {
4836 /* AWS v4 */
4837 version = AwsVersion::V4;
4838 } else if (!strncmp(http_auth, "AWS ", 4)) {
4839 /* AWS v2 */
4840 version = AwsVersion::V2;
4841 }
4842 } else {
4843 route = AwsRoute::QUERY_STRING;
4844
4845 if (info.args.get("x-amz-algorithm") == AWS4_HMAC_SHA256_STR) {
4846 /* AWS v4 */
4847 version = AwsVersion::V4;
4848 } else if (!info.args.get("AWSAccessKeyId").empty()) {
4849 /* AWS v2 */
4850 version = AwsVersion::V2;
4851 }
4852 }
4853
4854 return std::make_pair(version, route);
4855 }
4856
4857 /*
4858 * verify that a signed request comes from the keyholder
4859 * by checking the signature against our locally-computed version
4860 *
4861 * it tries AWS v4 before AWS v2
4862 */
4863 int RGW_Auth_S3::authorize(const DoutPrefixProvider *dpp,
4864 rgw::sal::Store* const store,
4865 const rgw::auth::StrategyRegistry& auth_registry,
4866 struct req_state* const s, optional_yield y)
4867 {
4868
4869 /* neither keystone and rados enabled; warn and exit! */
4870 if (!store->ctx()->_conf->rgw_s3_auth_use_rados &&
4871 !store->ctx()->_conf->rgw_s3_auth_use_keystone &&
4872 !store->ctx()->_conf->rgw_s3_auth_use_ldap) {
4873 ldpp_dout(dpp, 0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl;
4874 return -EPERM;
4875 }
4876
4877 const auto ret = rgw::auth::Strategy::apply(dpp, auth_registry.get_s3_main(), s, y);
4878 if (ret == 0) {
4879 /* Populate the owner info. */
4880 s->owner.set_id(s->user->get_id());
4881 s->owner.set_name(s->user->get_display_name());
4882 }
4883 return ret;
4884 }
4885
4886 int RGWHandler_Auth_S3::init(rgw::sal::Store* store, struct req_state *state,
4887 rgw::io::BasicClient *cio)
4888 {
4889 int ret = RGWHandler_REST_S3::init_from_header(store, state, RGW_FORMAT_JSON, true);
4890 if (ret < 0)
4891 return ret;
4892
4893 return RGWHandler_REST::init(store, state, cio);
4894 }
4895
4896 RGWHandler_REST* RGWRESTMgr_S3::get_handler(rgw::sal::Store* store,
4897 struct req_state* const s,
4898 const rgw::auth::StrategyRegistry& auth_registry,
4899 const std::string& frontend_prefix)
4900 {
4901 bool is_s3website = enable_s3website && (s->prot_flags & RGW_REST_WEBSITE);
4902 int ret =
4903 RGWHandler_REST_S3::init_from_header(store, s,
4904 is_s3website ? RGW_FORMAT_HTML :
4905 RGW_FORMAT_XML, true);
4906 if (ret < 0)
4907 return NULL;
4908
4909 RGWHandler_REST* handler;
4910 // TODO: Make this more readable
4911 if (is_s3website) {
4912 if (s->init_state.url_bucket.empty()) {
4913 handler = new RGWHandler_REST_Service_S3Website(auth_registry);
4914 } else if (rgw::sal::Object::empty(s->object.get())) {
4915 handler = new RGWHandler_REST_Bucket_S3Website(auth_registry);
4916 } else {
4917 handler = new RGWHandler_REST_Obj_S3Website(auth_registry);
4918 }
4919 } else {
4920 if (s->init_state.url_bucket.empty()) {
4921 handler = new RGWHandler_REST_Service_S3(auth_registry, enable_sts, enable_iam, enable_pubsub);
4922 } else if (!rgw::sal::Object::empty(s->object.get())) {
4923 handler = new RGWHandler_REST_Obj_S3(auth_registry);
4924 } else if (s->info.args.exist_obj_excl_sub_resource()) {
4925 return NULL;
4926 } else {
4927 handler = new RGWHandler_REST_Bucket_S3(auth_registry, enable_pubsub);
4928 }
4929 }
4930
4931 ldpp_dout(s, 20) << __func__ << " handler=" << typeid(*handler).name()
4932 << dendl;
4933 return handler;
4934 }
4935
4936 bool RGWHandler_REST_S3Website::web_dir() const {
4937 std::string subdir_name;
4938 if (!rgw::sal::Object::empty(s->object.get())) {
4939 subdir_name = url_decode(s->object->get_name());
4940 }
4941
4942 if (subdir_name.empty()) {
4943 return false;
4944 } else if (subdir_name.back() == '/' && subdir_name.size() > 1) {
4945 subdir_name.pop_back();
4946 }
4947
4948 std::unique_ptr<rgw::sal::Object> obj = s->bucket->get_object(rgw_obj_key(subdir_name));
4949
4950 RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
4951 obj->set_atomic(&obj_ctx);
4952 obj->set_prefetch_data(&obj_ctx);
4953
4954 RGWObjState* state = nullptr;
4955 if (obj->get_obj_state(s, &obj_ctx, &state, s->yield) < 0) {
4956 return false;
4957 }
4958 if (! state->exists) {
4959 return false;
4960 }
4961 return state->exists;
4962 }
4963
4964 int RGWHandler_REST_S3Website::init(rgw::sal::Store* store, req_state *s,
4965 rgw::io::BasicClient* cio)
4966 {
4967 // save the original object name before retarget() replaces it with the
4968 // result of get_effective_key(). the error_handler() needs the original
4969 // object name for redirect handling
4970 if (!rgw::sal::Object::empty(s->object.get())) {
4971 original_object_name = s->object->get_name();
4972 } else {
4973 original_object_name = "";
4974 }
4975
4976 return RGWHandler_REST_S3::init(store, s, cio);
4977 }
4978
4979 int RGWHandler_REST_S3Website::retarget(RGWOp* op, RGWOp** new_op, optional_yield y) {
4980 *new_op = op;
4981 ldpp_dout(s, 10) << __func__ << " Starting retarget" << dendl;
4982
4983 if (!(s->prot_flags & RGW_REST_WEBSITE))
4984 return 0;
4985
4986 if (!s->bucket->get_info().has_website) {
4987 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
4988 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION;
4989 }
4990
4991 rgw_obj_key new_obj;
4992 string key_name;
4993 if (!rgw::sal::Object::empty(s->object.get())) {
4994 key_name = s->object->get_name();
4995 }
4996 bool get_res = s->bucket->get_info().website_conf.get_effective_key(key_name, &new_obj.name, web_dir());
4997 if (!get_res) {
4998 s->err.message = "The IndexDocument Suffix is not configurated or not well formed!";
4999 ldpp_dout(s, 5) << s->err.message << dendl;
5000 return -EINVAL;
5001 }
5002
5003 ldpp_dout(s, 10) << "retarget get_effective_key " << s->object << " -> "
5004 << new_obj << dendl;
5005
5006 RGWBWRoutingRule rrule;
5007 bool should_redirect =
5008 s->bucket->get_info().website_conf.should_redirect(new_obj.name, 0, &rrule);
5009
5010 if (should_redirect) {
5011 const string& hostname = s->info.env->get("HTTP_HOST", "");
5012 const string& protocol =
5013 (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
5014 int redirect_code = 0;
5015 rrule.apply_rule(protocol, hostname, key_name, &s->redirect,
5016 &redirect_code);
5017 // APply a custom HTTP response code
5018 if (redirect_code > 0)
5019 s->err.http_ret = redirect_code; // Apply a custom HTTP response code
5020 ldpp_dout(s, 10) << "retarget redirect code=" << redirect_code
5021 << " proto+host:" << protocol << "://" << hostname
5022 << " -> " << s->redirect << dendl;
5023 return -ERR_WEBSITE_REDIRECT;
5024 }
5025
5026 /*
5027 * FIXME: if s->object != new_obj, drop op and create a new op to handle
5028 * operation. Or remove this comment if it's not applicable anymore
5029 * dang: This could be problematic, since we're not actually replacing op, but
5030 * we are replacing s->object. Something might have a pointer to it.
5031 */
5032 s->object = s->bucket->get_object(new_obj);
5033
5034 return 0;
5035 }
5036
5037 RGWOp* RGWHandler_REST_S3Website::op_get()
5038 {
5039 return get_obj_op(true);
5040 }
5041
5042 RGWOp* RGWHandler_REST_S3Website::op_head()
5043 {
5044 return get_obj_op(false);
5045 }
5046
5047 int RGWHandler_REST_S3Website::serve_errordoc(const DoutPrefixProvider *dpp, int http_ret, const string& errordoc_key, optional_yield y) {
5048 int ret = 0;
5049 s->formatter->reset(); /* Try to throw it all away */
5050
5051 std::shared_ptr<RGWGetObj_ObjStore_S3Website> getop( static_cast<RGWGetObj_ObjStore_S3Website*>(op_get()));
5052 if (getop.get() == NULL) {
5053 return -1; // Trigger double error handler
5054 }
5055 getop->init(store, s, this);
5056 getop->range_str = NULL;
5057 getop->if_mod = NULL;
5058 getop->if_unmod = NULL;
5059 getop->if_match = NULL;
5060 getop->if_nomatch = NULL;
5061 /* This is okay. It's an error, so nothing will run after this, and it can be
5062 * called by abort_early(), which can be called before s->object or s->bucket
5063 * are set up. Note, it won't have bucket. */
5064 s->object = store->get_object(errordoc_key);
5065
5066 ret = init_permissions(getop.get(), y);
5067 if (ret < 0) {
5068 ldpp_dout(s, 20) << "serve_errordoc failed, init_permissions ret=" << ret << dendl;
5069 return -1; // Trigger double error handler
5070 }
5071
5072 ret = read_permissions(getop.get(), y);
5073 if (ret < 0) {
5074 ldpp_dout(s, 20) << "serve_errordoc failed, read_permissions ret=" << ret << dendl;
5075 return -1; // Trigger double error handler
5076 }
5077
5078 if (http_ret) {
5079 getop->set_custom_http_response(http_ret);
5080 }
5081
5082 ret = getop->init_processing(y);
5083 if (ret < 0) {
5084 ldpp_dout(s, 20) << "serve_errordoc failed, init_processing ret=" << ret << dendl;
5085 return -1; // Trigger double error handler
5086 }
5087
5088 ret = getop->verify_op_mask();
5089 if (ret < 0) {
5090 ldpp_dout(s, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret << dendl;
5091 return -1; // Trigger double error handler
5092 }
5093
5094 ret = getop->verify_permission(y);
5095 if (ret < 0) {
5096 ldpp_dout(s, 20) << "serve_errordoc failed, verify_permission ret=" << ret << dendl;
5097 return -1; // Trigger double error handler
5098 }
5099
5100 ret = getop->verify_params();
5101 if (ret < 0) {
5102 ldpp_dout(s, 20) << "serve_errordoc failed, verify_params ret=" << ret << dendl;
5103 return -1; // Trigger double error handler
5104 }
5105
5106 // No going back now
5107 getop->pre_exec();
5108 /*
5109 * FIXME Missing headers:
5110 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
5111 * x-amz-error-code: NoSuchKey
5112 * x-amz-error-message: The specified key does not exist.
5113 * x-amz-error-detail-Key: foo
5114 */
5115 getop->execute(y);
5116 getop->complete();
5117 return 0;
5118 }
5119
5120 int RGWHandler_REST_S3Website::error_handler(int err_no,
5121 string* error_content,
5122 optional_yield y) {
5123 int new_err_no = -1;
5124 rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no > 0 ? err_no : -err_no);
5125 int http_error_code = -1;
5126
5127 if (r != rgw_http_s3_errors.end()) {
5128 http_error_code = r->second.first;
5129 }
5130 ldpp_dout(s, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no << " http_ret=" << http_error_code << dendl;
5131
5132 RGWBWRoutingRule rrule;
5133 bool have_bucket = !rgw::sal::Bucket::empty(s->bucket.get());
5134 bool should_redirect = false;
5135 if (have_bucket) {
5136 should_redirect =
5137 s->bucket->get_info().website_conf.should_redirect(original_object_name,
5138 http_error_code, &rrule);
5139 }
5140
5141 if (should_redirect) {
5142 const string& hostname = s->info.env->get("HTTP_HOST", "");
5143 const string& protocol =
5144 (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
5145 int redirect_code = 0;
5146 rrule.apply_rule(protocol, hostname, original_object_name,
5147 &s->redirect, &redirect_code);
5148 // Apply a custom HTTP response code
5149 if (redirect_code > 0)
5150 s->err.http_ret = redirect_code; // Apply a custom HTTP response code
5151 ldpp_dout(s, 10) << "error handler redirect code=" << redirect_code
5152 << " proto+host:" << protocol << "://" << hostname
5153 << " -> " << s->redirect << dendl;
5154 return -ERR_WEBSITE_REDIRECT;
5155 } else if (err_no == -ERR_WEBSITE_REDIRECT) {
5156 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
5157 // Do NOT fire the ErrorDoc handler
5158 } else if (have_bucket && !s->bucket->get_info().website_conf.error_doc.empty()) {
5159 /* This serves an entire page!
5160 On success, it will return zero, and no further content should be sent to the socket
5161 On failure, we need the double-error handler
5162 */
5163 new_err_no = RGWHandler_REST_S3Website::serve_errordoc(s, http_error_code, s->bucket->get_info().website_conf.error_doc, y);
5164 if (new_err_no != -1) {
5165 err_no = new_err_no;
5166 }
5167 } else {
5168 ldpp_dout(s, 20) << "No special error handling today!" << dendl;
5169 }
5170
5171 return err_no;
5172 }
5173
5174 RGWOp* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data)
5175 {
5176 /** If we are in website mode, then it is explicitly impossible to run GET or
5177 * HEAD on the actual directory. We must convert the request to run on the
5178 * suffix object instead!
5179 */
5180 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
5181 op->set_get_data(get_data);
5182 return op;
5183 }
5184
5185 RGWOp* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data)
5186 {
5187 /** If we are in website mode, then it is explicitly impossible to run GET or
5188 * HEAD on the actual directory. We must convert the request to run on the
5189 * suffix object instead!
5190 */
5191 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
5192 op->set_get_data(get_data);
5193 return op;
5194 }
5195
5196 RGWOp* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data)
5197 {
5198 /** If we are in website mode, then it is explicitly impossible to run GET or
5199 * HEAD on the actual directory. We must convert the request to run on the
5200 * suffix object instead!
5201 */
5202 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
5203 op->set_get_data(get_data);
5204 return op;
5205 }
5206
5207
5208 namespace rgw::auth::s3 {
5209
5210 static rgw::auth::Completer::cmplptr_t
5211 null_completer_factory(const boost::optional<std::string>& secret_key)
5212 {
5213 return nullptr;
5214 }
5215
5216
5217 AWSEngine::VersionAbstractor::auth_data_t
5218 AWSGeneralAbstractor::get_auth_data(const req_state* const s) const
5219 {
5220 AwsVersion version;
5221 AwsRoute route;
5222 std::tie(version, route) = discover_aws_flavour(s->info);
5223
5224 if (version == AwsVersion::V2) {
5225 return get_auth_data_v2(s);
5226 } else if (version == AwsVersion::V4) {
5227 return get_auth_data_v4(s, route == AwsRoute::QUERY_STRING);
5228 } else {
5229 /* FIXME(rzarzynski): handle anon user. */
5230 throw -EINVAL;
5231 }
5232 }
5233
5234 boost::optional<std::string>
5235 AWSGeneralAbstractor::get_v4_canonical_headers(
5236 const req_info& info,
5237 const std::string_view& signedheaders,
5238 const bool using_qs) const
5239 {
5240 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
5241 using_qs, false);
5242 }
5243
5244 AWSSignerV4::prepare_result_t
5245 AWSSignerV4::prepare(const DoutPrefixProvider *dpp,
5246 const std::string& access_key_id,
5247 const string& region,
5248 const string& service,
5249 const req_info& info,
5250 const bufferlist *opt_content,
5251 bool s3_op)
5252 {
5253 std::string signed_hdrs;
5254
5255 ceph::real_time timestamp = ceph::real_clock::now();
5256
5257 map<string, string> extra_headers;
5258
5259 std::string date = ceph::to_iso_8601_no_separators(timestamp, ceph::iso_8601_format::YMDhms);
5260
5261 std::string credential_scope = gen_v4_scope(timestamp, region, service);
5262
5263 extra_headers["x-amz-date"] = date;
5264
5265 string content_hash;
5266
5267 if (opt_content) {
5268 content_hash = rgw::auth::s3::calc_v4_payload_hash(opt_content->to_str());
5269 extra_headers["x-amz-content-sha256"] = content_hash;
5270
5271 }
5272
5273 /* craft canonical headers */
5274 std::string canonical_headers = \
5275 gen_v4_canonical_headers(info, extra_headers, &signed_hdrs);
5276
5277 using sanitize = rgw::crypt_sanitize::log_content;
5278 ldpp_dout(dpp, 10) << "canonical headers format = "
5279 << sanitize{canonical_headers} << dendl;
5280
5281 bool is_non_s3_op = !s3_op;
5282
5283 const char* exp_payload_hash = nullptr;
5284 string payload_hash;
5285 if (is_non_s3_op) {
5286 //For non s3 ops, we need to calculate the payload hash
5287 payload_hash = info.args.get("PayloadHash");
5288 exp_payload_hash = payload_hash.c_str();
5289 } else {
5290 /* Get the expected hash. */
5291 if (content_hash.empty()) {
5292 exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(info);
5293 } else {
5294 exp_payload_hash = content_hash.c_str();
5295 }
5296 }
5297
5298 /* Craft canonical URI. Using std::move later so let it be non-const. */
5299 auto canonical_uri = rgw::auth::s3::gen_v4_canonical_uri(info);
5300
5301
5302 /* Craft canonical query string. std::moving later so non-const here. */
5303 auto canonical_qs = rgw::auth::s3::gen_v4_canonical_qs(info);
5304
5305 auto cct = dpp->get_cct();
5306
5307 /* Craft canonical request. */
5308 auto canonical_req_hash = \
5309 rgw::auth::s3::get_v4_canon_req_hash(cct,
5310 info.method,
5311 std::move(canonical_uri),
5312 std::move(canonical_qs),
5313 std::move(canonical_headers),
5314 signed_hdrs,
5315 exp_payload_hash,
5316 dpp);
5317
5318 auto string_to_sign = \
5319 rgw::auth::s3::get_v4_string_to_sign(cct,
5320 AWS4_HMAC_SHA256_STR,
5321 date,
5322 credential_scope,
5323 std::move(canonical_req_hash),
5324 dpp);
5325
5326 const auto sig_factory = gen_v4_signature;
5327
5328 /* Requests authenticated with the Query Parameters are treated as unsigned.
5329 * From "Authenticating Requests: Using Query Parameters (AWS Signature
5330 * Version 4)":
5331 *
5332 * You don't include a payload hash in the Canonical Request, because
5333 * when you create a presigned URL, you don't know the payload content
5334 * because the URL is used to upload an arbitrary payload. Instead, you
5335 * use a constant string UNSIGNED-PAYLOAD.
5336 *
5337 * This means we have absolutely no business in spawning completer. Both
5338 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
5339 * by default. We don't need to change that. */
5340 return {
5341 access_key_id,
5342 date,
5343 credential_scope,
5344 std::move(signed_hdrs),
5345 std::move(string_to_sign),
5346 std::move(extra_headers),
5347 sig_factory,
5348 };
5349 }
5350
5351 AWSSignerV4::signature_headers_t
5352 gen_v4_signature(const DoutPrefixProvider *dpp,
5353 const std::string_view& secret_key,
5354 const AWSSignerV4::prepare_result_t& sig_info)
5355 {
5356 auto signature = rgw::auth::s3::get_v4_signature(sig_info.scope,
5357 dpp->get_cct(),
5358 secret_key,
5359 sig_info.string_to_sign,
5360 dpp);
5361 AWSSignerV4::signature_headers_t result;
5362
5363 for (auto& entry : sig_info.extra_headers) {
5364 result[entry.first] = entry.second;
5365 }
5366 auto& payload_hash = result["x-amz-content-sha256"];
5367 if (payload_hash.empty()) {
5368 payload_hash = AWS4_UNSIGNED_PAYLOAD_HASH;
5369 }
5370 string auth_header = string("AWS4-HMAC-SHA256 Credential=").append(sig_info.access_key_id) + "/";
5371 auth_header.append(sig_info.scope + ",SignedHeaders=")
5372 .append(sig_info.signed_headers + ",Signature=")
5373 .append(signature);
5374 result["Authorization"] = auth_header;
5375
5376 return result;
5377 }
5378
5379
5380 AWSEngine::VersionAbstractor::auth_data_t
5381 AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
5382 const bool using_qs) const
5383 {
5384 std::string_view access_key_id;
5385 std::string_view signed_hdrs;
5386
5387 std::string_view date;
5388 std::string_view credential_scope;
5389 std::string_view client_signature;
5390 std::string_view session_token;
5391
5392 int ret = rgw::auth::s3::parse_v4_credentials(s->info,
5393 access_key_id,
5394 credential_scope,
5395 signed_hdrs,
5396 client_signature,
5397 date,
5398 session_token,
5399 using_qs,
5400 s);
5401 if (ret < 0) {
5402 throw ret;
5403 }
5404
5405 /* craft canonical headers */
5406 boost::optional<std::string> canonical_headers = \
5407 get_v4_canonical_headers(s->info, signed_hdrs, using_qs);
5408 if (canonical_headers) {
5409 using sanitize = rgw::crypt_sanitize::log_content;
5410 ldpp_dout(s, 10) << "canonical headers format = "
5411 << sanitize{*canonical_headers} << dendl;
5412 } else {
5413 throw -EPERM;
5414 }
5415
5416 bool is_non_s3_op = rgw::auth::s3::is_non_s3_op(s->op_type);
5417
5418 const char* exp_payload_hash = nullptr;
5419 string payload_hash;
5420 if (is_non_s3_op) {
5421 //For non s3 ops, we need to calculate the payload hash
5422 payload_hash = s->info.args.get("PayloadHash");
5423 exp_payload_hash = payload_hash.c_str();
5424 } else {
5425 /* Get the expected hash. */
5426 exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);
5427 }
5428
5429 /* Craft canonical URI. Using std::move later so let it be non-const. */
5430 auto canonical_uri = rgw::auth::s3::get_v4_canonical_uri(s->info);
5431
5432 /* Craft canonical query string. std::moving later so non-const here. */
5433 auto canonical_qs = rgw::auth::s3::get_v4_canonical_qs(s->info, using_qs);
5434
5435 /* Craft canonical request. */
5436 auto canonical_req_hash = \
5437 rgw::auth::s3::get_v4_canon_req_hash(s->cct,
5438 s->info.method,
5439 std::move(canonical_uri),
5440 std::move(canonical_qs),
5441 std::move(*canonical_headers),
5442 signed_hdrs,
5443 exp_payload_hash,
5444 s);
5445
5446 auto string_to_sign = \
5447 rgw::auth::s3::get_v4_string_to_sign(s->cct,
5448 AWS4_HMAC_SHA256_STR,
5449 date,
5450 credential_scope,
5451 std::move(canonical_req_hash),
5452 s);
5453
5454 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
5455 credential_scope,
5456 std::placeholders::_1,
5457 std::placeholders::_2,
5458 std::placeholders::_3,
5459 s);
5460
5461 /* Requests authenticated with the Query Parameters are treated as unsigned.
5462 * From "Authenticating Requests: Using Query Parameters (AWS Signature
5463 * Version 4)":
5464 *
5465 * You don't include a payload hash in the Canonical Request, because
5466 * when you create a presigned URL, you don't know the payload content
5467 * because the URL is used to upload an arbitrary payload. Instead, you
5468 * use a constant string UNSIGNED-PAYLOAD.
5469 *
5470 * This means we have absolutely no business in spawning completer. Both
5471 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
5472 * by default. We don't need to change that. */
5473 if (is_v4_payload_unsigned(exp_payload_hash) || is_v4_payload_empty(s) || is_non_s3_op) {
5474 return {
5475 access_key_id,
5476 client_signature,
5477 session_token,
5478 std::move(string_to_sign),
5479 sig_factory,
5480 null_completer_factory
5481 };
5482 } else {
5483 /* We're going to handle a signed payload. Be aware that even empty HTTP
5484 * body (no payload) requires verification:
5485 *
5486 * The x-amz-content-sha256 header is required for all AWS Signature
5487 * Version 4 requests. It provides a hash of the request payload. If
5488 * there is no payload, you must provide the hash of an empty string. */
5489 if (!is_v4_payload_streamed(exp_payload_hash)) {
5490 ldpp_dout(s, 10) << "delaying v4 auth" << dendl;
5491
5492 /* payload in a single chunk */
5493 switch (s->op_type)
5494 {
5495 case RGW_OP_CREATE_BUCKET:
5496 case RGW_OP_PUT_OBJ:
5497 case RGW_OP_PUT_ACLS:
5498 case RGW_OP_PUT_CORS:
5499 case RGW_OP_PUT_BUCKET_ENCRYPTION:
5500 case RGW_OP_GET_BUCKET_ENCRYPTION:
5501 case RGW_OP_DELETE_BUCKET_ENCRYPTION:
5502 case RGW_OP_INIT_MULTIPART: // in case that Init Multipart uses CHUNK encoding
5503 case RGW_OP_COMPLETE_MULTIPART:
5504 case RGW_OP_SET_BUCKET_VERSIONING:
5505 case RGW_OP_DELETE_MULTI_OBJ:
5506 case RGW_OP_ADMIN_SET_METADATA:
5507 case RGW_OP_SYNC_DATALOG_NOTIFY:
5508 case RGW_OP_SYNC_MDLOG_NOTIFY:
5509 case RGW_OP_PERIOD_POST:
5510 case RGW_OP_SET_BUCKET_WEBSITE:
5511 case RGW_OP_PUT_BUCKET_POLICY:
5512 case RGW_OP_PUT_OBJ_TAGGING:
5513 case RGW_OP_PUT_BUCKET_TAGGING:
5514 case RGW_OP_PUT_BUCKET_REPLICATION:
5515 case RGW_OP_PUT_LC:
5516 case RGW_OP_SET_REQUEST_PAYMENT:
5517 case RGW_OP_PUBSUB_NOTIF_CREATE:
5518 case RGW_OP_PUBSUB_NOTIF_DELETE:
5519 case RGW_OP_PUBSUB_NOTIF_LIST:
5520 case RGW_OP_PUT_BUCKET_OBJ_LOCK:
5521 case RGW_OP_PUT_OBJ_RETENTION:
5522 case RGW_OP_PUT_OBJ_LEGAL_HOLD:
5523 case RGW_STS_GET_SESSION_TOKEN:
5524 case RGW_STS_ASSUME_ROLE:
5525 case RGW_OP_PUT_BUCKET_PUBLIC_ACCESS_BLOCK:
5526 case RGW_OP_GET_BUCKET_PUBLIC_ACCESS_BLOCK:
5527 case RGW_OP_DELETE_BUCKET_PUBLIC_ACCESS_BLOCK:
5528 case RGW_OP_GET_OBJ://s3select its post-method(payload contain the query) , the request is get-object
5529 break;
5530 default:
5531 ldpp_dout(s, 10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
5532 throw -ERR_NOT_IMPLEMENTED;
5533 }
5534
5535 const auto cmpl_factory = std::bind(AWSv4ComplSingle::create,
5536 s,
5537 std::placeholders::_1);
5538 return {
5539 access_key_id,
5540 client_signature,
5541 session_token,
5542 std::move(string_to_sign),
5543 sig_factory,
5544 cmpl_factory
5545 };
5546 } else {
5547 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
5548 * it "chunked" but let's be coherent with Amazon's terminology. */
5549
5550 ldpp_dout(s, 10) << "body content detected in multiple chunks" << dendl;
5551
5552 /* payload in multiple chunks */
5553
5554 switch(s->op_type)
5555 {
5556 case RGW_OP_PUT_OBJ:
5557 break;
5558 default:
5559 ldpp_dout(s, 10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl;
5560 throw -ERR_NOT_IMPLEMENTED;
5561 }
5562
5563 ldpp_dout(s, 10) << "aws4 seed signature ok... delaying v4 auth" << dendl;
5564
5565 /* In the case of streamed payload client sets the x-amz-content-sha256
5566 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
5567 * when constructing the Canonical Request. */
5568
5569 /* In the case of single-chunk upload client set the header's value is
5570 * coherent with the one used for Canonical Request crafting. */
5571
5572 /* In the case of query string-based authentication there should be no
5573 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
5574 * for CanonReq. */
5575 const auto cmpl_factory = std::bind(AWSv4ComplMulti::create,
5576 s,
5577 date,
5578 credential_scope,
5579 client_signature,
5580 std::placeholders::_1);
5581 return {
5582 access_key_id,
5583 client_signature,
5584 session_token,
5585 std::move(string_to_sign),
5586 sig_factory,
5587 cmpl_factory
5588 };
5589 }
5590 }
5591 }
5592
5593
5594 boost::optional<std::string>
5595 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
5596 const req_info& info,
5597 const std::string_view& signedheaders,
5598 const bool using_qs) const
5599 {
5600 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
5601 using_qs, true);
5602 }
5603
5604
5605 AWSEngine::VersionAbstractor::auth_data_t
5606 AWSGeneralAbstractor::get_auth_data_v2(const req_state* const s) const
5607 {
5608 std::string_view access_key_id;
5609 std::string_view signature;
5610 std::string_view session_token;
5611 bool qsr = false;
5612
5613 const char* http_auth = s->info.env->get("HTTP_AUTHORIZATION");
5614 if (! http_auth || http_auth[0] == '\0') {
5615 /* Credentials are provided in query string. We also need to verify
5616 * the "Expires" parameter now. */
5617 access_key_id = s->info.args.get("AWSAccessKeyId");
5618 signature = s->info.args.get("Signature");
5619 qsr = true;
5620
5621 std::string_view expires = s->info.args.get("Expires");
5622 if (expires.empty()) {
5623 throw -EPERM;
5624 }
5625
5626 /* It looks we have the guarantee that expires is a null-terminated,
5627 * and thus string_view::data() can be safely used. */
5628 const time_t exp = atoll(expires.data());
5629 time_t now;
5630 time(&now);
5631
5632 if (now >= exp) {
5633 throw -EPERM;
5634 }
5635 if (s->info.args.exists("x-amz-security-token")) {
5636 session_token = s->info.args.get("x-amz-security-token");
5637 if (session_token.size() == 0) {
5638 throw -EPERM;
5639 }
5640 }
5641
5642 } else {
5643 /* The "Authorization" HTTP header is being used. */
5644 const std::string_view auth_str(http_auth + strlen("AWS "));
5645 const size_t pos = auth_str.rfind(':');
5646 if (pos != std::string_view::npos) {
5647 access_key_id = auth_str.substr(0, pos);
5648 signature = auth_str.substr(pos + 1);
5649 }
5650
5651 if (s->info.env->exists("HTTP_X_AMZ_SECURITY_TOKEN")) {
5652 session_token = s->info.env->get("HTTP_X_AMZ_SECURITY_TOKEN");
5653 if (session_token.size() == 0) {
5654 throw -EPERM;
5655 }
5656 }
5657 }
5658
5659 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
5660 std::string string_to_sign;
5661 utime_t header_time;
5662 if (! rgw_create_s3_canonical_header(s, s->info, &header_time, string_to_sign,
5663 qsr)) {
5664 ldpp_dout(s, 10) << "failed to create the canonized auth header\n"
5665 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
5666 throw -EPERM;
5667 }
5668
5669 ldpp_dout(s, 10) << "string_to_sign:\n"
5670 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
5671
5672 if (!qsr && !is_time_skew_ok(header_time)) {
5673 throw -ERR_REQUEST_TIME_SKEWED;
5674 }
5675
5676 return {
5677 std::move(access_key_id),
5678 std::move(signature),
5679 std::move(session_token),
5680 std::move(string_to_sign),
5681 rgw::auth::s3::get_v2_signature,
5682 null_completer_factory
5683 };
5684 }
5685
5686
5687 AWSEngine::VersionAbstractor::auth_data_t
5688 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state* const s) const
5689 {
5690 return {
5691 s->auth.s3_postobj_creds.access_key,
5692 s->auth.s3_postobj_creds.signature,
5693 s->auth.s3_postobj_creds.x_amz_security_token,
5694 s->auth.s3_postobj_creds.encoded_policy.to_str(),
5695 rgw::auth::s3::get_v2_signature,
5696 null_completer_factory
5697 };
5698 }
5699
5700 AWSEngine::VersionAbstractor::auth_data_t
5701 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state* const s) const
5702 {
5703 const std::string_view credential = s->auth.s3_postobj_creds.x_amz_credential;
5704
5705 /* grab access key id */
5706 const size_t pos = credential.find("/");
5707 const std::string_view access_key_id = credential.substr(0, pos);
5708 ldpp_dout(s, 10) << "access key id = " << access_key_id << dendl;
5709
5710 /* grab credential scope */
5711 const std::string_view credential_scope = credential.substr(pos + 1);
5712 ldpp_dout(s, 10) << "credential scope = " << credential_scope << dendl;
5713
5714 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
5715 credential_scope,
5716 std::placeholders::_1,
5717 std::placeholders::_2,
5718 std::placeholders::_3,
5719 s);
5720
5721 return {
5722 access_key_id,
5723 s->auth.s3_postobj_creds.signature,
5724 s->auth.s3_postobj_creds.x_amz_security_token,
5725 s->auth.s3_postobj_creds.encoded_policy.to_str(),
5726 sig_factory,
5727 null_completer_factory
5728 };
5729 }
5730
5731 AWSEngine::VersionAbstractor::auth_data_t
5732 AWSBrowserUploadAbstractor::get_auth_data(const req_state* const s) const
5733 {
5734 if (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR) {
5735 ldpp_dout(s, 0) << "Signature verification algorithm AWS v4"
5736 << " (AWS4-HMAC-SHA256)" << dendl;
5737 return get_auth_data_v4(s);
5738 } else {
5739 ldpp_dout(s, 0) << "Signature verification algorithm AWS v2" << dendl;
5740 return get_auth_data_v2(s);
5741 }
5742 }
5743
5744 AWSEngine::result_t
5745 AWSEngine::authenticate(const DoutPrefixProvider* dpp, const req_state* const s, optional_yield y) const
5746 {
5747 /* Small reminder: an ver_abstractor is allowed to throw! */
5748 const auto auth_data = ver_abstractor.get_auth_data(s);
5749
5750 if (auth_data.access_key_id.empty() || auth_data.client_signature.empty()) {
5751 return result_t::deny(-EINVAL);
5752 } else {
5753 return authenticate(dpp,
5754 auth_data.access_key_id,
5755 auth_data.client_signature,
5756 auth_data.session_token,
5757 auth_data.string_to_sign,
5758 auth_data.signature_factory,
5759 auth_data.completer_factory,
5760 s, y);
5761 }
5762 }
5763
5764 } // namespace rgw::auth::s3
5765
5766 rgw::LDAPHelper* rgw::auth::s3::LDAPEngine::ldh = nullptr;
5767 std::mutex rgw::auth::s3::LDAPEngine::mtx;
5768
5769 void rgw::auth::s3::LDAPEngine::init(CephContext* const cct)
5770 {
5771 if (! cct->_conf->rgw_s3_auth_use_ldap ||
5772 cct->_conf->rgw_ldap_uri.empty()) {
5773 return;
5774 }
5775
5776 if (! ldh) {
5777 std::lock_guard<std::mutex> lck(mtx);
5778 if (! ldh) {
5779 const string& ldap_uri = cct->_conf->rgw_ldap_uri;
5780 const string& ldap_binddn = cct->_conf->rgw_ldap_binddn;
5781 const string& ldap_searchdn = cct->_conf->rgw_ldap_searchdn;
5782 const string& ldap_searchfilter = cct->_conf->rgw_ldap_searchfilter;
5783 const string& ldap_dnattr = cct->_conf->rgw_ldap_dnattr;
5784 std::string ldap_bindpw = parse_rgw_ldap_bindpw(cct);
5785
5786 ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw,
5787 ldap_searchdn, ldap_searchfilter, ldap_dnattr);
5788
5789 ldh->init();
5790 ldh->bind();
5791 }
5792 }
5793 }
5794
5795 bool rgw::auth::s3::LDAPEngine::valid() {
5796 std::lock_guard<std::mutex> lck(mtx);
5797 return (!!ldh);
5798 }
5799
5800 rgw::auth::RemoteApplier::acl_strategy_t
5801 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
5802 {
5803 //This is based on the assumption that the default acl strategy in
5804 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
5805 return nullptr;
5806 }
5807
5808 rgw::auth::RemoteApplier::AuthInfo
5809 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noexcept
5810 {
5811 /* The short form of "using" can't be used here -- we're aliasing a class'
5812 * member. */
5813 using acct_privilege_t = \
5814 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
5815
5816 return rgw::auth::RemoteApplier::AuthInfo {
5817 rgw_user(token.id),
5818 token.id,
5819 RGW_PERM_FULL_CONTROL,
5820 acct_privilege_t::IS_PLAIN_ACCT,
5821 TYPE_LDAP
5822 };
5823 }
5824
5825 rgw::auth::Engine::result_t
5826 rgw::auth::s3::LDAPEngine::authenticate(
5827 const DoutPrefixProvider* dpp,
5828 const std::string_view& access_key_id,
5829 const std::string_view& signature,
5830 const std::string_view& session_token,
5831 const string_to_sign_t& string_to_sign,
5832 const signature_factory_t&,
5833 const completer_factory_t& completer_factory,
5834 const req_state* const s,
5835 optional_yield y) const
5836 {
5837 /* boost filters and/or string_ref may throw on invalid input */
5838 rgw::RGWToken base64_token;
5839 try {
5840 base64_token = rgw::from_base64(access_key_id);
5841 } catch (...) {
5842 base64_token = std::string("");
5843 }
5844
5845 if (! base64_token.valid()) {
5846 return result_t::deny();
5847 }
5848
5849 //TODO: Uncomment, when we have a migration plan in place.
5850 //Check if a user of type other than 'ldap' is already present, if yes, then
5851 //return error.
5852 /*RGWUserInfo user_info;
5853 user_info.user_id = base64_token.id;
5854 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
5855 if (user_info.type != TYPE_LDAP) {
5856 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
5857 return nullptr;
5858 }
5859 }*/
5860
5861 if (ldh->auth(base64_token.id, base64_token.key) != 0) {
5862 return result_t::deny(-ERR_INVALID_ACCESS_KEY);
5863 }
5864
5865 auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
5866 get_creds_info(base64_token));
5867 return result_t::grant(std::move(apl), completer_factory(boost::none));
5868 } /* rgw::auth::s3::LDAPEngine::authenticate */
5869
5870 void rgw::auth::s3::LDAPEngine::shutdown() {
5871 if (ldh) {
5872 delete ldh;
5873 ldh = nullptr;
5874 }
5875 }
5876
5877 /* LocalEngine */
5878 rgw::auth::Engine::result_t
5879 rgw::auth::s3::LocalEngine::authenticate(
5880 const DoutPrefixProvider* dpp,
5881 const std::string_view& _access_key_id,
5882 const std::string_view& signature,
5883 const std::string_view& session_token,
5884 const string_to_sign_t& string_to_sign,
5885 const signature_factory_t& signature_factory,
5886 const completer_factory_t& completer_factory,
5887 const req_state* const s,
5888 optional_yield y) const
5889 {
5890 /* get the user info */
5891 std::unique_ptr<rgw::sal::User> user;
5892 const std::string access_key_id(_access_key_id);
5893 /* TODO(rzarzynski): we need to have string-view taking variant. */
5894 if (store->get_user_by_access_key(dpp, access_key_id, y, &user) < 0) {
5895 ldpp_dout(dpp, 5) << "error reading user info, uid=" << access_key_id
5896 << " can't authenticate" << dendl;
5897 return result_t::deny(-ERR_INVALID_ACCESS_KEY);
5898 }
5899 //TODO: Uncomment, when we have a migration plan in place.
5900 /*else {
5901 if (s->user->type != TYPE_RGW) {
5902 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << s->user->type
5903 << " is present" << dendl;
5904 throw -EPERM;
5905 }
5906 }*/
5907
5908 const auto iter = user->get_info().access_keys.find(access_key_id);
5909 if (iter == std::end(user->get_info().access_keys)) {
5910 ldpp_dout(dpp, 0) << "ERROR: access key not encoded in user info" << dendl;
5911 return result_t::deny(-EPERM);
5912 }
5913 const RGWAccessKey& k = iter->second;
5914
5915 const VersionAbstractor::server_signature_t server_signature = \
5916 signature_factory(cct, k.key, string_to_sign);
5917 auto compare = signature.compare(server_signature);
5918
5919 ldpp_dout(dpp, 15) << "string_to_sign="
5920 << rgw::crypt_sanitize::log_content{string_to_sign}
5921 << dendl;
5922 ldpp_dout(dpp, 15) << "server signature=" << server_signature << dendl;
5923 ldpp_dout(dpp, 15) << "client signature=" << signature << dendl;
5924 ldpp_dout(dpp, 15) << "compare=" << compare << dendl;
5925
5926 if (compare != 0) {
5927 return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
5928 }
5929
5930 auto apl = apl_factory->create_apl_local(cct, s, user->get_info(),
5931 k.subuser, std::nullopt);
5932 return result_t::grant(std::move(apl), completer_factory(k.key));
5933 }
5934
5935 rgw::auth::RemoteApplier::AuthInfo
5936 rgw::auth::s3::STSEngine::get_creds_info(const STS::SessionToken& token) const noexcept
5937 {
5938 using acct_privilege_t = \
5939 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
5940
5941 return rgw::auth::RemoteApplier::AuthInfo {
5942 token.user,
5943 token.acct_name,
5944 token.perm_mask,
5945 (token.is_admin) ? acct_privilege_t::IS_ADMIN_ACCT: acct_privilege_t::IS_PLAIN_ACCT,
5946 token.acct_type
5947 };
5948 }
5949
5950 int
5951 rgw::auth::s3::STSEngine::get_session_token(const DoutPrefixProvider* dpp, const std::string_view& session_token,
5952 STS::SessionToken& token) const
5953 {
5954 string decodedSessionToken;
5955 try {
5956 decodedSessionToken = rgw::from_base64(session_token);
5957 } catch (...) {
5958 ldpp_dout(dpp, 0) << "ERROR: Invalid session token, not base64 encoded." << dendl;
5959 return -EINVAL;
5960 }
5961
5962 auto* cryptohandler = cct->get_crypto_handler(CEPH_CRYPTO_AES);
5963 if (! cryptohandler) {
5964 return -EINVAL;
5965 }
5966 string secret_s = cct->_conf->rgw_sts_key;
5967 buffer::ptr secret(secret_s.c_str(), secret_s.length());
5968 int ret = 0;
5969 if (ret = cryptohandler->validate_secret(secret); ret < 0) {
5970 ldpp_dout(dpp, 0) << "ERROR: Invalid secret key" << dendl;
5971 return -EINVAL;
5972 }
5973 string error;
5974 std::unique_ptr<CryptoKeyHandler> keyhandler(cryptohandler->get_key_handler(secret, error));
5975 if (! keyhandler) {
5976 return -EINVAL;
5977 }
5978 error.clear();
5979
5980 string decrypted_str;
5981 buffer::list en_input, dec_output;
5982 en_input = buffer::list::static_from_string(decodedSessionToken);
5983
5984 ret = keyhandler->decrypt(en_input, dec_output, &error);
5985 if (ret < 0) {
5986 ldpp_dout(dpp, 0) << "ERROR: Decryption failed: " << error << dendl;
5987 return -EPERM;
5988 } else {
5989 try {
5990 dec_output.append('\0');
5991 auto iter = dec_output.cbegin();
5992 decode(token, iter);
5993 } catch (const buffer::error& e) {
5994 ldpp_dout(dpp, 0) << "ERROR: decode SessionToken failed: " << error << dendl;
5995 return -EINVAL;
5996 }
5997 }
5998 return 0;
5999 }
6000
6001 rgw::auth::Engine::result_t
6002 rgw::auth::s3::STSEngine::authenticate(
6003 const DoutPrefixProvider* dpp,
6004 const std::string_view& _access_key_id,
6005 const std::string_view& signature,
6006 const std::string_view& session_token,
6007 const string_to_sign_t& string_to_sign,
6008 const signature_factory_t& signature_factory,
6009 const completer_factory_t& completer_factory,
6010 const req_state* const s,
6011 optional_yield y) const
6012 {
6013 if (! s->info.args.exists("x-amz-security-token") &&
6014 ! s->info.env->exists("HTTP_X_AMZ_SECURITY_TOKEN") &&
6015 s->auth.s3_postobj_creds.x_amz_security_token.empty()) {
6016 return result_t::deny();
6017 }
6018
6019 STS::SessionToken token;
6020 if (int ret = get_session_token(dpp, session_token, token); ret < 0) {
6021 return result_t::reject(ret);
6022 }
6023 //Authentication
6024 //Check if access key is not the same passed in by client
6025 if (token.access_key_id != _access_key_id) {
6026 ldpp_dout(dpp, 0) << "Invalid access key" << dendl;
6027 return result_t::reject(-EPERM);
6028 }
6029 //Check if the token has expired
6030 if (! token.expiration.empty()) {
6031 std::string expiration = token.expiration;
6032 if (! expiration.empty()) {
6033 boost::optional<real_clock::time_point> exp = ceph::from_iso_8601(expiration, false);
6034 if (exp) {
6035 real_clock::time_point now = real_clock::now();
6036 if (now >= *exp) {
6037 ldpp_dout(dpp, 0) << "ERROR: Token expired" << dendl;
6038 return result_t::reject(-EPERM);
6039 }
6040 } else {
6041 ldpp_dout(dpp, 0) << "ERROR: Invalid expiration: " << expiration << dendl;
6042 return result_t::reject(-EPERM);
6043 }
6044 }
6045 }
6046 //Check for signature mismatch
6047 const VersionAbstractor::server_signature_t server_signature = \
6048 signature_factory(cct, token.secret_access_key, string_to_sign);
6049 auto compare = signature.compare(server_signature);
6050
6051 ldpp_dout(dpp, 15) << "string_to_sign="
6052 << rgw::crypt_sanitize::log_content{string_to_sign}
6053 << dendl;
6054 ldpp_dout(dpp, 15) << "server signature=" << server_signature << dendl;
6055 ldpp_dout(dpp, 15) << "client signature=" << signature << dendl;
6056 ldpp_dout(dpp, 15) << "compare=" << compare << dendl;
6057
6058 if (compare != 0) {
6059 return result_t::reject(-ERR_SIGNATURE_NO_MATCH);
6060 }
6061
6062 // Get all the authorization info
6063 std::unique_ptr<rgw::sal::User> user;
6064 rgw_user user_id;
6065 string role_id;
6066 rgw::auth::RoleApplier::Role r;
6067 rgw::auth::RoleApplier::TokenAttrs t_attrs;
6068 if (! token.roleId.empty()) {
6069 std::unique_ptr<rgw::sal::RGWRole> role = store->get_role(token.roleId);
6070 if (role->get_by_id(dpp, y) < 0) {
6071 return result_t::deny(-EPERM);
6072 }
6073 r.id = token.roleId;
6074 r.name = role->get_name();
6075 r.tenant = role->get_tenant();
6076
6077 vector<string> role_policy_names = role->get_role_policy_names();
6078 for (auto& policy_name : role_policy_names) {
6079 string perm_policy;
6080 if (int ret = role->get_role_policy(dpp, policy_name, perm_policy); ret == 0) {
6081 r.role_policies.push_back(std::move(perm_policy));
6082 }
6083 }
6084 }
6085
6086 user = store->get_user(token.user);
6087 if (! token.user.empty() && token.acct_type != TYPE_ROLE) {
6088 // get user info
6089 int ret = user->load_user(dpp, y);
6090 if (ret < 0) {
6091 ldpp_dout(dpp, 5) << "ERROR: failed reading user info: uid=" << token.user << dendl;
6092 return result_t::reject(-EPERM);
6093 }
6094 }
6095
6096 if (token.acct_type == TYPE_KEYSTONE || token.acct_type == TYPE_LDAP) {
6097 auto apl = remote_apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
6098 get_creds_info(token));
6099 return result_t::grant(std::move(apl), completer_factory(token.secret_access_key));
6100 } else if (token.acct_type == TYPE_ROLE) {
6101 t_attrs.user_id = std::move(token.user); // This is mostly needed to assign the owner of a bucket during its creation
6102 t_attrs.token_policy = std::move(token.policy);
6103 t_attrs.role_session_name = std::move(token.role_session);
6104 t_attrs.token_claims = std::move(token.token_claims);
6105 t_attrs.token_issued_at = std::move(token.issued_at);
6106 t_attrs.principal_tags = std::move(token.principal_tags);
6107 auto apl = role_apl_factory->create_apl_role(cct, s, r, t_attrs);
6108 return result_t::grant(std::move(apl), completer_factory(token.secret_access_key));
6109 } else { // This is for all local users of type TYPE_RGW or TYPE_NONE
6110 string subuser;
6111 auto apl = local_apl_factory->create_apl_local(cct, s, user->get_info(), subuser, token.perm_mask);
6112 return result_t::grant(std::move(apl), completer_factory(token.secret_access_key));
6113 }
6114 }
6115
6116 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
6117 const req_state* s
6118 ) const noexcept {
6119 if (s->op == OP_OPTIONS) {
6120 return true;
6121 }
6122
6123 AwsVersion version;
6124 AwsRoute route;
6125 std::tie(version, route) = discover_aws_flavour(s->info);
6126
6127 return route == AwsRoute::QUERY_STRING && version == AwsVersion::UNKNOWN;
6128 }
6129