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