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