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