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