]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_common.cc
import ceph pacific 16.2.5
[ceph.git] / ceph / src / rgw / rgw_common.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3
4 #include <errno.h>
5 #include <vector>
6 #include <algorithm>
7 #include <string>
8 #include <boost/tokenizer.hpp>
9
10 #include "json_spirit/json_spirit.h"
11 #include "common/ceph_json.h"
12
13 #include "rgw_op.h"
14 #include "rgw_common.h"
15 #include "rgw_acl.h"
16 #include "rgw_string.h"
17 #include "rgw_http_errors.h"
18 #include "rgw_arn.h"
19 #include "rgw_data_sync.h"
20
21 #include "common/ceph_crypto.h"
22 #include "common/armor.h"
23 #include "common/errno.h"
24 #include "common/Clock.h"
25 #include "common/Formatter.h"
26 #include "common/convenience.h"
27 #include "common/strtol.h"
28 #include "include/str_list.h"
29 #include "rgw_crypt_sanitize.h"
30 #include "rgw_bucket_sync.h"
31 #include "rgw_sync_policy.h"
32
33 #include "services/svc_zone.h"
34
35 #include <sstream>
36
37 #define dout_context g_ceph_context
38 #define dout_subsys ceph_subsys_rgw
39
40 using rgw::ARN;
41 using rgw::IAM::Effect;
42 using rgw::IAM::op_to_perm;
43 using rgw::IAM::Policy;
44
45 const uint32_t RGWBucketInfo::NUM_SHARDS_BLIND_BUCKET(UINT32_MAX);
46
47 rgw_http_errors rgw_http_s3_errors({
48 { 0, {200, "" }},
49 { STATUS_CREATED, {201, "Created" }},
50 { STATUS_ACCEPTED, {202, "Accepted" }},
51 { STATUS_NO_CONTENT, {204, "NoContent" }},
52 { STATUS_PARTIAL_CONTENT, {206, "" }},
53 { ERR_PERMANENT_REDIRECT, {301, "PermanentRedirect" }},
54 { ERR_WEBSITE_REDIRECT, {301, "WebsiteRedirect" }},
55 { STATUS_REDIRECT, {303, "" }},
56 { ERR_NOT_MODIFIED, {304, "NotModified" }},
57 { EINVAL, {400, "InvalidArgument" }},
58 { ERR_INVALID_REQUEST, {400, "InvalidRequest" }},
59 { ERR_INVALID_DIGEST, {400, "InvalidDigest" }},
60 { ERR_BAD_DIGEST, {400, "BadDigest" }},
61 { ERR_INVALID_LOCATION_CONSTRAINT, {400, "InvalidLocationConstraint" }},
62 { ERR_ZONEGROUP_DEFAULT_PLACEMENT_MISCONFIGURATION, {400, "ZonegroupDefaultPlacementMisconfiguration" }},
63 { ERR_INVALID_BUCKET_NAME, {400, "InvalidBucketName" }},
64 { ERR_INVALID_OBJECT_NAME, {400, "InvalidObjectName" }},
65 { ERR_UNRESOLVABLE_EMAIL, {400, "UnresolvableGrantByEmailAddress" }},
66 { ERR_INVALID_PART, {400, "InvalidPart" }},
67 { ERR_INVALID_PART_ORDER, {400, "InvalidPartOrder" }},
68 { ERR_REQUEST_TIMEOUT, {400, "RequestTimeout" }},
69 { ERR_TOO_LARGE, {400, "EntityTooLarge" }},
70 { ERR_TOO_SMALL, {400, "EntityTooSmall" }},
71 { ERR_TOO_MANY_BUCKETS, {400, "TooManyBuckets" }},
72 { ERR_MALFORMED_XML, {400, "MalformedXML" }},
73 { ERR_AMZ_CONTENT_SHA256_MISMATCH, {400, "XAmzContentSHA256Mismatch" }},
74 { ERR_MALFORMED_DOC, {400, "MalformedPolicyDocument"}},
75 { ERR_INVALID_TAG, {400, "InvalidTag"}},
76 { ERR_MALFORMED_ACL_ERROR, {400, "MalformedACLError" }},
77 { ERR_INVALID_CORS_RULES_ERROR, {400, "InvalidRequest" }},
78 { ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR, {400, "InvalidRequest" }},
79 { ERR_INVALID_ENCRYPTION_ALGORITHM, {400, "InvalidEncryptionAlgorithmError" }},
80 { ERR_INVALID_RETENTION_PERIOD,{400, "InvalidRetentionPeriod"}},
81 { ERR_LIMIT_EXCEEDED, {400, "LimitExceeded" }},
82 { ERR_LENGTH_REQUIRED, {411, "MissingContentLength" }},
83 { EACCES, {403, "AccessDenied" }},
84 { EPERM, {403, "AccessDenied" }},
85 { ERR_SIGNATURE_NO_MATCH, {403, "SignatureDoesNotMatch" }},
86 { ERR_INVALID_ACCESS_KEY, {403, "InvalidAccessKeyId" }},
87 { ERR_USER_SUSPENDED, {403, "UserSuspended" }},
88 { ERR_REQUEST_TIME_SKEWED, {403, "RequestTimeTooSkewed" }},
89 { ERR_QUOTA_EXCEEDED, {403, "QuotaExceeded" }},
90 { ERR_MFA_REQUIRED, {403, "AccessDenied" }},
91 { ENOENT, {404, "NoSuchKey" }},
92 { ERR_NO_SUCH_BUCKET, {404, "NoSuchBucket" }},
93 { ERR_NO_SUCH_WEBSITE_CONFIGURATION, {404, "NoSuchWebsiteConfiguration" }},
94 { ERR_NO_SUCH_UPLOAD, {404, "NoSuchUpload" }},
95 { ERR_NOT_FOUND, {404, "Not Found"}},
96 { ERR_NO_SUCH_LC, {404, "NoSuchLifecycleConfiguration"}},
97 { ERR_NO_SUCH_BUCKET_POLICY, {404, "NoSuchBucketPolicy"}},
98 { ERR_NO_SUCH_USER, {404, "NoSuchUser"}},
99 { ERR_NO_ROLE_FOUND, {404, "NoSuchEntity"}},
100 { ERR_NO_CORS_FOUND, {404, "NoSuchCORSConfiguration"}},
101 { ERR_NO_SUCH_SUBUSER, {404, "NoSuchSubUser"}},
102 { ERR_NO_SUCH_ENTITY, {404, "NoSuchEntity"}},
103 { ERR_NO_SUCH_CORS_CONFIGURATION, {404, "NoSuchCORSConfiguration"}},
104 { ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION, {404, "ObjectLockConfigurationNotFoundError"}},
105 { ERR_METHOD_NOT_ALLOWED, {405, "MethodNotAllowed" }},
106 { ETIMEDOUT, {408, "RequestTimeout" }},
107 { EEXIST, {409, "BucketAlreadyExists" }},
108 { ERR_USER_EXIST, {409, "UserAlreadyExists" }},
109 { ERR_EMAIL_EXIST, {409, "EmailExists" }},
110 { ERR_KEY_EXIST, {409, "KeyExists"}},
111 { ERR_TAG_CONFLICT, {409, "OperationAborted"}},
112 { ERR_POSITION_NOT_EQUAL_TO_LENGTH, {409, "PositionNotEqualToLength"}},
113 { ERR_OBJECT_NOT_APPENDABLE, {409, "ObjectNotAppendable"}},
114 { ERR_INVALID_BUCKET_STATE, {409, "InvalidBucketState"}},
115 { ERR_INVALID_SECRET_KEY, {400, "InvalidSecretKey"}},
116 { ERR_INVALID_KEY_TYPE, {400, "InvalidKeyType"}},
117 { ERR_INVALID_CAP, {400, "InvalidCapability"}},
118 { ERR_INVALID_TENANT_NAME, {400, "InvalidTenantName" }},
119 { ENOTEMPTY, {409, "BucketNotEmpty" }},
120 { ERR_PRECONDITION_FAILED, {412, "PreconditionFailed" }},
121 { ERANGE, {416, "InvalidRange" }},
122 { ERR_UNPROCESSABLE_ENTITY, {422, "UnprocessableEntity" }},
123 { ERR_LOCKED, {423, "Locked" }},
124 { ERR_INTERNAL_ERROR, {500, "InternalError" }},
125 { ERR_NOT_IMPLEMENTED, {501, "NotImplemented" }},
126 { ERR_SERVICE_UNAVAILABLE, {503, "ServiceUnavailable"}},
127 { ERR_RATE_LIMITED, {503, "SlowDown"}},
128 { ERR_ZERO_IN_URL, {400, "InvalidRequest" }},
129 { ERR_NO_SUCH_TAG_SET, {404, "NoSuchTagSetError"}},
130 });
131
132 rgw_http_errors rgw_http_swift_errors({
133 { EACCES, {403, "AccessDenied" }},
134 { EPERM, {401, "AccessDenied" }},
135 { ENAMETOOLONG, {400, "Metadata name too long" }},
136 { ERR_USER_SUSPENDED, {401, "UserSuspended" }},
137 { ERR_INVALID_UTF8, {412, "Invalid UTF8" }},
138 { ERR_BAD_URL, {412, "Bad URL" }},
139 { ERR_NOT_SLO_MANIFEST, {400, "Not an SLO manifest" }},
140 { ERR_QUOTA_EXCEEDED, {413, "QuotaExceeded" }},
141 { ENOTEMPTY, {409, "There was a conflict when trying "
142 "to complete your request." }},
143 /* FIXME(rzarzynski): we need to find a way to apply Swift's error handling
144 * procedures also for ERR_ZERO_IN_URL. This make a problem as the validation
145 * is performed very early, even before setting the req_state::proto_flags. */
146 { ERR_ZERO_IN_URL, {412, "Invalid UTF8 or contains NULL"}},
147 { ERR_RATE_LIMITED, {498, "Rate Limited"}},
148 });
149
150 rgw_http_errors rgw_http_sts_errors({
151 { ERR_PACKED_POLICY_TOO_LARGE, {400, "PackedPolicyTooLarge" }},
152 { ERR_INVALID_IDENTITY_TOKEN, {400, "InvalidIdentityToken" }},
153 });
154
155 rgw_http_errors rgw_http_iam_errors({
156 { EINVAL, {400, "InvalidInput" }},
157 { ENOENT, {404, "NoSuchEntity"}},
158 { ERR_ROLE_EXISTS, {409, "EntityAlreadyExists"}},
159 { ERR_DELETE_CONFLICT, {409, "DeleteConflict"}},
160 { EEXIST, {409, "EntityAlreadyExists"}},
161 { ERR_INTERNAL_ERROR, {500, "ServiceFailure" }},
162 });
163
164 using namespace ceph::crypto;
165
166 rgw_err::
167 rgw_err()
168 {
169 clear();
170 }
171
172 void rgw_err::
173 clear()
174 {
175 http_ret = 200;
176 ret = 0;
177 err_code.clear();
178 }
179
180 bool rgw_err::
181 is_clear() const
182 {
183 return (http_ret == 200);
184 }
185
186 bool rgw_err::
187 is_err() const
188 {
189 return !(http_ret >= 200 && http_ret <= 399);
190 }
191
192 // The requestURI transferred from the frontend can be abs_path or absoluteURI
193 // If it is absoluteURI, we should adjust it to abs_path for the following
194 // S3 authorization and some other processes depending on the requestURI
195 // The absoluteURI can start with "http://", "https://", "ws://" or "wss://"
196 static string get_abs_path(const string& request_uri) {
197 const static string ABS_PREFIXS[] = {"http://", "https://", "ws://", "wss://"};
198 bool isAbs = false;
199 for (int i = 0; i < 4; ++i) {
200 if (boost::algorithm::starts_with(request_uri, ABS_PREFIXS[i])) {
201 isAbs = true;
202 break;
203 }
204 }
205 if (!isAbs) { // it is not a valid absolute uri
206 return request_uri;
207 }
208 size_t beg_pos = request_uri.find("://") + 3;
209 size_t len = request_uri.size();
210 beg_pos = request_uri.find('/', beg_pos);
211 if (beg_pos == string::npos) return request_uri;
212 return request_uri.substr(beg_pos, len - beg_pos);
213 }
214
215 req_info::req_info(CephContext *cct, const class RGWEnv *env) : env(env) {
216 method = env->get("REQUEST_METHOD", "");
217 script_uri = env->get("SCRIPT_URI", cct->_conf->rgw_script_uri.c_str());
218 request_uri = env->get("REQUEST_URI", cct->_conf->rgw_request_uri.c_str());
219 if (request_uri[0] != '/') {
220 request_uri = get_abs_path(request_uri);
221 }
222 auto pos = request_uri.find('?');
223 if (pos != string::npos) {
224 request_params = request_uri.substr(pos + 1);
225 request_uri = request_uri.substr(0, pos);
226 } else {
227 request_params = env->get("QUERY_STRING", "");
228 }
229 host = env->get("HTTP_HOST", "");
230
231 // strip off any trailing :port from host (added by CrossFTP and maybe others)
232 size_t colon_offset = host.find_last_of(':');
233 if (colon_offset != string::npos) {
234 bool all_digits = true;
235 for (unsigned i = colon_offset + 1; i < host.size(); ++i) {
236 if (!isdigit(host[i])) {
237 all_digits = false;
238 break;
239 }
240 }
241 if (all_digits) {
242 host.resize(colon_offset);
243 }
244 }
245 }
246
247 void req_info::rebuild_from(req_info& src)
248 {
249 method = src.method;
250 script_uri = src.script_uri;
251 args = src.args;
252 if (src.effective_uri.empty()) {
253 request_uri = src.request_uri;
254 } else {
255 request_uri = src.effective_uri;
256 }
257 effective_uri.clear();
258 host = src.host;
259
260 x_meta_map = src.x_meta_map;
261 x_meta_map.erase("x-amz-date");
262 }
263
264
265 req_state::req_state(CephContext* _cct, RGWEnv* e, uint64_t id)
266 : cct(_cct), info(_cct, e), id(id)
267 {
268 enable_ops_log = e->get_enable_ops_log();
269 enable_usage_log = e->get_enable_usage_log();
270 defer_to_bucket_acls = e->get_defer_to_bucket_acls();
271
272 time = Clock::now();
273 }
274
275 req_state::~req_state() {
276 delete formatter;
277 }
278
279 std::ostream& req_state::gen_prefix(std::ostream& out) const
280 {
281 auto p = out.precision();
282 return out << "req " << id << ' '
283 << std::setprecision(3) << std::fixed << time_elapsed() // '0.123s'
284 << std::setprecision(p) << std::defaultfloat << ' ';
285 }
286
287 bool search_err(rgw_http_errors& errs, int err_no, int& http_ret, string& code)
288 {
289 auto r = errs.find(err_no);
290 if (r != errs.end()) {
291 http_ret = r->second.first;
292 code = r->second.second;
293 return true;
294 }
295 return false;
296 }
297
298 void set_req_state_err(struct rgw_err& err, /* out */
299 int err_no, /* in */
300 const int prot_flags) /* in */
301 {
302 if (err_no < 0)
303 err_no = -err_no;
304
305 err.ret = -err_no;
306
307 if (prot_flags & RGW_REST_SWIFT) {
308 if (search_err(rgw_http_swift_errors, err_no, err.http_ret, err.err_code))
309 return;
310 }
311
312 if (prot_flags & RGW_REST_STS) {
313 if (search_err(rgw_http_sts_errors, err_no, err.http_ret, err.err_code))
314 return;
315 }
316
317 if (prot_flags & RGW_REST_IAM) {
318 if (search_err(rgw_http_iam_errors, err_no, err.http_ret, err.err_code))
319 return;
320 }
321
322 //Default to searching in s3 errors
323 if (search_err(rgw_http_s3_errors, err_no, err.http_ret, err.err_code))
324 return;
325 dout(0) << "WARNING: set_req_state_err err_no=" << err_no
326 << " resorting to 500" << dendl;
327
328 err.http_ret = 500;
329 err.err_code = "UnknownError";
330 }
331
332 void set_req_state_err(struct req_state* s, int err_no, const string& err_msg)
333 {
334 if (s) {
335 set_req_state_err(s, err_no);
336 if (s->prot_flags & RGW_REST_SWIFT && !err_msg.empty()) {
337 /* TODO(rzarzynski): there never ever should be a check like this one.
338 * It's here only for the sake of the patch's backportability. Further
339 * commits will move the logic to a per-RGWHandler replacement of
340 * the end_header() function. Alternativaly, we might consider making
341 * that just for the dump(). Please take a look on @cbodley's comments
342 * in PR #10690 (https://github.com/ceph/ceph/pull/10690). */
343 s->err.err_code = err_msg;
344 } else {
345 s->err.message = err_msg;
346 }
347 }
348 }
349
350 void set_req_state_err(struct req_state* s, int err_no)
351 {
352 if (s) {
353 set_req_state_err(s->err, err_no, s->prot_flags);
354 }
355 }
356
357 void dump(struct req_state* s)
358 {
359 if (s->format != RGW_FORMAT_HTML)
360 s->formatter->open_object_section("Error");
361 if (!s->err.err_code.empty())
362 s->formatter->dump_string("Code", s->err.err_code);
363 if (!s->err.message.empty())
364 s->formatter->dump_string("Message", s->err.message);
365 if (!s->bucket_name.empty()) // TODO: connect to expose_bucket
366 s->formatter->dump_string("BucketName", s->bucket_name);
367 if (!s->trans_id.empty()) // TODO: connect to expose_bucket or another toggle
368 s->formatter->dump_string("RequestId", s->trans_id);
369 s->formatter->dump_string("HostId", s->host_id);
370 if (s->format != RGW_FORMAT_HTML)
371 s->formatter->close_section();
372 }
373
374 struct str_len {
375 const char *str;
376 int len;
377 };
378
379 #define STR_LEN_ENTRY(s) { s, sizeof(s) - 1 }
380
381 struct str_len meta_prefixes[] = { STR_LEN_ENTRY("HTTP_X_AMZ"),
382 STR_LEN_ENTRY("HTTP_X_GOOG"),
383 STR_LEN_ENTRY("HTTP_X_DHO"),
384 STR_LEN_ENTRY("HTTP_X_RGW"),
385 STR_LEN_ENTRY("HTTP_X_OBJECT"),
386 STR_LEN_ENTRY("HTTP_X_CONTAINER"),
387 STR_LEN_ENTRY("HTTP_X_ACCOUNT"),
388 {NULL, 0} };
389
390 void req_info::init_meta_info(const DoutPrefixProvider *dpp, bool *found_bad_meta)
391 {
392 x_meta_map.clear();
393
394 for (const auto& kv: env->get_map()) {
395 const char *prefix;
396 const string& header_name = kv.first;
397 const string& val = kv.second;
398 for (int prefix_num = 0; (prefix = meta_prefixes[prefix_num].str) != NULL; prefix_num++) {
399 int len = meta_prefixes[prefix_num].len;
400 const char *p = header_name.c_str();
401 if (strncmp(p, prefix, len) == 0) {
402 ldpp_dout(dpp, 10) << "meta>> " << p << dendl;
403 const char *name = p+len; /* skip the prefix */
404 int name_len = header_name.size() - len;
405
406 if (found_bad_meta && strncmp(name, "_META_", name_len) == 0)
407 *found_bad_meta = true;
408
409 char name_low[meta_prefixes[0].len + name_len + 1];
410 snprintf(name_low, meta_prefixes[0].len - 5 + name_len + 1, "%s%s", meta_prefixes[0].str + 5 /* skip HTTP_ */, name); // normalize meta prefix
411 int j;
412 for (j = 0; name_low[j]; j++) {
413 if (name_low[j] != '_')
414 name_low[j] = tolower(name_low[j]);
415 else
416 name_low[j] = '-';
417 }
418 name_low[j] = 0;
419
420 auto it = x_meta_map.find(name_low);
421 if (it != x_meta_map.end()) {
422 string old = it->second;
423 boost::algorithm::trim_right(old);
424 old.append(",");
425 old.append(val);
426 x_meta_map[name_low] = old;
427 } else {
428 x_meta_map[name_low] = val;
429 }
430 }
431 }
432 }
433 for (const auto& kv: x_meta_map) {
434 ldpp_dout(dpp, 10) << "x>> " << kv.first << ":" << rgw::crypt_sanitize::x_meta_map{kv.first, kv.second} << dendl;
435 }
436 }
437
438 std::ostream& operator<<(std::ostream& oss, const rgw_err &err)
439 {
440 oss << "rgw_err(http_ret=" << err.http_ret << ", err_code='" << err.err_code << "') ";
441 return oss;
442 }
443
444 void rgw_add_amz_meta_header(
445 meta_map_t& x_meta_map,
446 const std::string& k,
447 const std::string& v)
448 {
449 auto it = x_meta_map.find(k);
450 if (it != x_meta_map.end()) {
451 std::string old = it->second;
452 boost::algorithm::trim_right(old);
453 old.append(",");
454 old.append(v);
455 x_meta_map[k] = old;
456 } else {
457 x_meta_map[k] = v;
458 }
459 }
460
461 string rgw_string_unquote(const string& s)
462 {
463 if (s[0] != '"' || s.size() < 2)
464 return s;
465
466 int len;
467 for (len = s.size(); len > 2; --len) {
468 if (s[len - 1] != ' ')
469 break;
470 }
471
472 if (s[len-1] != '"')
473 return s;
474
475 return s.substr(1, len - 2);
476 }
477
478 static bool check_str_end(const char *s)
479 {
480 if (!s)
481 return false;
482
483 while (*s) {
484 if (!isspace(*s))
485 return false;
486 s++;
487 }
488 return true;
489 }
490
491 static bool check_gmt_end(const char *s)
492 {
493 if (!s || !*s)
494 return false;
495
496 while (isspace(*s)) {
497 ++s;
498 }
499
500 /* check for correct timezone */
501 if ((strncmp(s, "GMT", 3) != 0) &&
502 (strncmp(s, "UTC", 3) != 0)) {
503 return false;
504 }
505
506 return true;
507 }
508
509 static bool parse_rfc850(const char *s, struct tm *t)
510 {
511 // FIPS zeroization audit 20191115: this memset is not security related.
512 memset(t, 0, sizeof(*t));
513 return check_gmt_end(strptime(s, "%A, %d-%b-%y %H:%M:%S ", t));
514 }
515
516 static bool parse_asctime(const char *s, struct tm *t)
517 {
518 // FIPS zeroization audit 20191115: this memset is not security related.
519 memset(t, 0, sizeof(*t));
520 return check_str_end(strptime(s, "%a %b %d %H:%M:%S %Y", t));
521 }
522
523 static bool parse_rfc1123(const char *s, struct tm *t)
524 {
525 // FIPS zeroization audit 20191115: this memset is not security related.
526 memset(t, 0, sizeof(*t));
527 return check_gmt_end(strptime(s, "%a, %d %b %Y %H:%M:%S ", t));
528 }
529
530 static bool parse_rfc1123_alt(const char *s, struct tm *t)
531 {
532 // FIPS zeroization audit 20191115: this memset is not security related.
533 memset(t, 0, sizeof(*t));
534 return check_str_end(strptime(s, "%a, %d %b %Y %H:%M:%S %z", t));
535 }
536
537 bool parse_rfc2616(const char *s, struct tm *t)
538 {
539 return parse_rfc850(s, t) || parse_asctime(s, t) || parse_rfc1123(s, t) || parse_rfc1123_alt(s,t);
540 }
541
542 bool parse_iso8601(const char *s, struct tm *t, uint32_t *pns, bool extended_format)
543 {
544 // FIPS zeroization audit 20191115: this memset is not security related.
545 memset(t, 0, sizeof(*t));
546 const char *p;
547
548 if (!s)
549 s = "";
550
551 if (extended_format) {
552 p = strptime(s, "%Y-%m-%dT%T", t);
553 if (!p) {
554 p = strptime(s, "%Y-%m-%d %T", t);
555 }
556 } else {
557 p = strptime(s, "%Y%m%dT%H%M%S", t);
558 }
559 if (!p) {
560 dout(0) << "parse_iso8601 failed" << dendl;
561 return false;
562 }
563 const std::string_view str = rgw_trim_whitespace(std::string_view(p));
564 int len = str.size();
565
566 if (len == 0 || (len == 1 && str[0] == 'Z'))
567 return true;
568
569 if (str[0] != '.' ||
570 str[len - 1] != 'Z')
571 return false;
572
573 uint32_t ms;
574 std::string_view nsstr = str.substr(1, len - 2);
575 int r = stringtoul(std::string(nsstr), &ms);
576 if (r < 0)
577 return false;
578
579 if (!pns) {
580 return true;
581 }
582
583 if (nsstr.size() > 9) {
584 nsstr = nsstr.substr(0, 9);
585 }
586
587 uint64_t mul_table[] = { 0,
588 100000000LL,
589 10000000LL,
590 1000000LL,
591 100000LL,
592 10000LL,
593 1000LL,
594 100LL,
595 10LL,
596 1 };
597
598
599 *pns = ms * mul_table[nsstr.size()];
600
601 return true;
602 }
603
604 int parse_key_value(string& in_str, const char *delim, string& key, string& val)
605 {
606 if (delim == NULL)
607 return -EINVAL;
608
609 auto pos = in_str.find(delim);
610 if (pos == string::npos)
611 return -EINVAL;
612
613 key = rgw_trim_whitespace(in_str.substr(0, pos));
614 val = rgw_trim_whitespace(in_str.substr(pos + 1));
615
616 return 0;
617 }
618
619 int parse_key_value(string& in_str, string& key, string& val)
620 {
621 return parse_key_value(in_str, "=", key,val);
622 }
623
624 boost::optional<std::pair<std::string_view, std::string_view>>
625 parse_key_value(const std::string_view& in_str,
626 const std::string_view& delim)
627 {
628 const size_t pos = in_str.find(delim);
629 if (pos == std::string_view::npos) {
630 return boost::none;
631 }
632
633 const auto key = rgw_trim_whitespace(in_str.substr(0, pos));
634 const auto val = rgw_trim_whitespace(in_str.substr(pos + 1));
635
636 return std::make_pair(key, val);
637 }
638
639 boost::optional<std::pair<std::string_view, std::string_view>>
640 parse_key_value(const std::string_view& in_str)
641 {
642 return parse_key_value(in_str, "=");
643 }
644
645 int parse_time(const char *time_str, real_time *time)
646 {
647 struct tm tm;
648 uint32_t ns = 0;
649
650 if (!parse_rfc2616(time_str, &tm) && !parse_iso8601(time_str, &tm, &ns)) {
651 return -EINVAL;
652 }
653
654 time_t sec = internal_timegm(&tm);
655 *time = utime_t(sec, ns).to_real_time();
656
657 return 0;
658 }
659
660 #define TIME_BUF_SIZE 128
661
662 void rgw_to_iso8601(const real_time& t, char *dest, int buf_size)
663 {
664 utime_t ut(t);
665
666 char buf[TIME_BUF_SIZE];
667 struct tm result;
668 time_t epoch = ut.sec();
669 struct tm *tmp = gmtime_r(&epoch, &result);
670 if (tmp == NULL)
671 return;
672
673 if (strftime(buf, sizeof(buf), "%Y-%m-%dT%T", tmp) == 0)
674 return;
675
676 snprintf(dest, buf_size, "%s.%03dZ", buf, (int)(ut.usec() / 1000));
677 }
678
679 void rgw_to_iso8601(const real_time& t, string *dest)
680 {
681 char buf[TIME_BUF_SIZE];
682 rgw_to_iso8601(t, buf, sizeof(buf));
683 *dest = buf;
684 }
685
686
687 string rgw_to_asctime(const utime_t& t)
688 {
689 stringstream s;
690 t.asctime(s);
691 return s.str();
692 }
693
694 /*
695 * calculate the sha1 value of a given msg and key
696 */
697 void calc_hmac_sha1(const char *key, int key_len,
698 const char *msg, int msg_len, char *dest)
699 /* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
700 {
701 HMACSHA1 hmac((const unsigned char *)key, key_len);
702 hmac.Update((const unsigned char *)msg, msg_len);
703 hmac.Final((unsigned char *)dest);
704 }
705
706 /*
707 * calculate the sha256 value of a given msg and key
708 */
709 void calc_hmac_sha256(const char *key, int key_len,
710 const char *msg, int msg_len, char *dest)
711 {
712 char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
713
714 HMACSHA256 hmac((const unsigned char *)key, key_len);
715 hmac.Update((const unsigned char *)msg, msg_len);
716 hmac.Final((unsigned char *)hash_sha256);
717
718 memcpy(dest, hash_sha256, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE);
719 }
720
721 using ceph::crypto::SHA256;
722
723 /*
724 * calculate the sha256 hash value of a given msg
725 */
726 sha256_digest_t calc_hash_sha256(const std::string_view& msg)
727 {
728 sha256_digest_t hash;
729
730 SHA256 hasher;
731 hasher.Update(reinterpret_cast<const unsigned char*>(msg.data()), msg.size());
732 hasher.Final(hash.v);
733
734 return hash;
735 }
736
737 SHA256* calc_hash_sha256_open_stream()
738 {
739 return new SHA256;
740 }
741
742 void calc_hash_sha256_update_stream(SHA256 *hash, const char *msg, int len)
743 {
744 hash->Update((const unsigned char *)msg, len);
745 }
746
747 string calc_hash_sha256_close_stream(SHA256 **phash)
748 {
749 SHA256 *hash = *phash;
750 if (!hash) {
751 hash = calc_hash_sha256_open_stream();
752 }
753 char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
754
755 hash->Final((unsigned char *)hash_sha256);
756
757 char hex_str[(CEPH_CRYPTO_SHA256_DIGESTSIZE * 2) + 1];
758 buf_to_hex((unsigned char *)hash_sha256, CEPH_CRYPTO_SHA256_DIGESTSIZE, hex_str);
759
760 delete hash;
761 *phash = NULL;
762
763 return std::string(hex_str);
764 }
765
766 std::string calc_hash_sha256_restart_stream(SHA256 **phash)
767 {
768 const auto hash = calc_hash_sha256_close_stream(phash);
769 *phash = calc_hash_sha256_open_stream();
770
771 return hash;
772 }
773
774 int NameVal::parse()
775 {
776 auto delim_pos = str.find('=');
777 int ret = 0;
778
779 if (delim_pos == string::npos) {
780 name = str;
781 val = "";
782 ret = 1;
783 } else {
784 name = str.substr(0, delim_pos);
785 val = str.substr(delim_pos + 1);
786 }
787
788 return ret;
789 }
790
791 int RGWHTTPArgs::parse(const DoutPrefixProvider *dpp)
792 {
793 int pos = 0;
794 bool end = false;
795
796 if (str.empty())
797 return 0;
798
799 if (str[pos] == '?')
800 pos++;
801
802 while (!end) {
803 int fpos = str.find('&', pos);
804 if (fpos < pos) {
805 end = true;
806 fpos = str.size();
807 }
808 std::string nameval = url_decode(str.substr(pos, fpos - pos), true);
809 NameVal nv(std::move(nameval));
810 int ret = nv.parse();
811 if (ret >= 0) {
812 string& name = nv.get_name();
813 if (name.find("X-Amz-") != string::npos) {
814 std::for_each(name.begin(),
815 name.end(),
816 [](char &c){
817 if (c != '-') {
818 c = ::tolower(static_cast<unsigned char>(c));
819 }
820 });
821 }
822 string& val = nv.get_val();
823 ldpp_dout(dpp, 10) << "name: " << name << " val: " << val << dendl;
824 append(name, val);
825 }
826
827 pos = fpos + 1;
828 }
829
830 return 0;
831 }
832
833 void RGWHTTPArgs::append(const string& name, const string& val)
834 {
835 if (name.compare(0, sizeof(RGW_SYS_PARAM_PREFIX) - 1, RGW_SYS_PARAM_PREFIX) == 0) {
836 sys_val_map[name] = val;
837 } else {
838 val_map[name] = val;
839 }
840
841 if ((name.compare("acl") == 0) ||
842 (name.compare("cors") == 0) ||
843 (name.compare("notification") == 0) ||
844 (name.compare("location") == 0) ||
845 (name.compare("logging") == 0) ||
846 (name.compare("usage") == 0) ||
847 (name.compare("lifecycle") == 0) ||
848 (name.compare("delete") == 0) ||
849 (name.compare("uploads") == 0) ||
850 (name.compare("partNumber") == 0) ||
851 (name.compare("uploadId") == 0) ||
852 (name.compare("versionId") == 0) ||
853 (name.compare("start-date") == 0) ||
854 (name.compare("end-date") == 0) ||
855 (name.compare("versions") == 0) ||
856 (name.compare("versioning") == 0) ||
857 (name.compare("website") == 0) ||
858 (name.compare("requestPayment") == 0) ||
859 (name.compare("torrent") == 0) ||
860 (name.compare("tagging") == 0) ||
861 (name.compare("append") == 0) ||
862 (name.compare("position") == 0) ||
863 (name.compare("policyStatus") == 0) ||
864 (name.compare("publicAccessBlock") == 0)) {
865 sub_resources[name] = val;
866 } else if (name[0] == 'r') { // root of all evil
867 if ((name.compare("response-content-type") == 0) ||
868 (name.compare("response-content-language") == 0) ||
869 (name.compare("response-expires") == 0) ||
870 (name.compare("response-cache-control") == 0) ||
871 (name.compare("response-content-disposition") == 0) ||
872 (name.compare("response-content-encoding") == 0)) {
873 sub_resources[name] = val;
874 has_resp_modifier = true;
875 }
876 } else if ((name.compare("subuser") == 0) ||
877 (name.compare("key") == 0) ||
878 (name.compare("caps") == 0) ||
879 (name.compare("index") == 0) ||
880 (name.compare("policy") == 0) ||
881 (name.compare("quota") == 0) ||
882 (name.compare("list") == 0) ||
883 (name.compare("object") == 0) ||
884 (name.compare("sync") == 0)) {
885 if (!admin_subresource_added) {
886 sub_resources[name] = "";
887 admin_subresource_added = true;
888 }
889 }
890 }
891
892 const string& RGWHTTPArgs::get(const string& name, bool *exists) const
893 {
894 auto iter = val_map.find(name);
895 bool e = (iter != std::end(val_map));
896 if (exists)
897 *exists = e;
898 if (e)
899 return iter->second;
900 return empty_str;
901 }
902
903 boost::optional<const std::string&>
904 RGWHTTPArgs::get_optional(const std::string& name) const
905 {
906 bool exists;
907 const std::string& value = get(name, &exists);
908 if (exists) {
909 return value;
910 } else {
911 return boost::none;
912 }
913 }
914
915 int RGWHTTPArgs::get_bool(const string& name, bool *val, bool *exists)
916 {
917 map<string, string>::iterator iter;
918 iter = val_map.find(name);
919 bool e = (iter != val_map.end());
920 if (exists)
921 *exists = e;
922
923 if (e) {
924 const char *s = iter->second.c_str();
925
926 if (strcasecmp(s, "false") == 0) {
927 *val = false;
928 } else if (strcasecmp(s, "true") == 0) {
929 *val = true;
930 } else {
931 return -EINVAL;
932 }
933 }
934
935 return 0;
936 }
937
938 int RGWHTTPArgs::get_bool(const char *name, bool *val, bool *exists)
939 {
940 string s(name);
941 return get_bool(s, val, exists);
942 }
943
944 void RGWHTTPArgs::get_bool(const char *name, bool *val, bool def_val)
945 {
946 bool exists = false;
947 if ((get_bool(name, val, &exists) < 0) ||
948 !exists) {
949 *val = def_val;
950 }
951 }
952
953 int RGWHTTPArgs::get_int(const char *name, int *val, int def_val)
954 {
955 bool exists = false;
956 string val_str;
957 val_str = get(name, &exists);
958 if (!exists) {
959 *val = def_val;
960 return 0;
961 }
962
963 string err;
964
965 *val = (int)strict_strtol(val_str.c_str(), 10, &err);
966 if (!err.empty()) {
967 *val = def_val;
968 return -EINVAL;
969 }
970 return 0;
971 }
972
973 string RGWHTTPArgs::sys_get(const string& name, bool * const exists) const
974 {
975 const auto iter = sys_val_map.find(name);
976 const bool e = (iter != sys_val_map.end());
977
978 if (exists) {
979 *exists = e;
980 }
981
982 return e ? iter->second : string();
983 }
984
985 bool rgw_transport_is_secure(CephContext *cct, const RGWEnv& env)
986 {
987 const auto& m = env.get_map();
988 // frontend connected with ssl
989 if (m.count("SERVER_PORT_SECURE")) {
990 return true;
991 }
992 // ignore proxy headers unless explicitly enabled
993 if (!cct->_conf->rgw_trust_forwarded_https) {
994 return false;
995 }
996 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
997 // Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>
998 auto i = m.find("HTTP_FORWARDED");
999 if (i != m.end() && i->second.find("proto=https") != std::string::npos) {
1000 return true;
1001 }
1002 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
1003 i = m.find("HTTP_X_FORWARDED_PROTO");
1004 if (i != m.end() && i->second == "https") {
1005 return true;
1006 }
1007 return false;
1008 }
1009
1010
1011 namespace {
1012
1013 struct perm_state_from_req_state : public perm_state_base {
1014 req_state * const s;
1015 perm_state_from_req_state(req_state * const _s)
1016 : perm_state_base(_s->cct,
1017 _s->env,
1018 _s->auth.identity.get(),
1019 _s->bucket.get() ? _s->bucket->get_info() : RGWBucketInfo(),
1020 _s->perm_mask,
1021 _s->defer_to_bucket_acls,
1022 _s->bucket_access_conf),
1023 s(_s) {}
1024
1025 std::optional<bool> get_request_payer() const override {
1026 const char *request_payer = s->info.env->get("HTTP_X_AMZ_REQUEST_PAYER");
1027 if (!request_payer) {
1028 bool exists;
1029 request_payer = s->info.args.get("x-amz-request-payer", &exists).c_str();
1030 if (!exists) {
1031 return false;
1032 }
1033 }
1034
1035 if (strcasecmp(request_payer, "requester") == 0) {
1036 return true;
1037 }
1038
1039 return std::nullopt;
1040 }
1041
1042 const char *get_referer() const override {
1043 return s->info.env->get("HTTP_REFERER");
1044 }
1045 };
1046
1047 Effect eval_or_pass(const boost::optional<Policy>& policy,
1048 const rgw::IAM::Environment& env,
1049 boost::optional<const rgw::auth::Identity&> id,
1050 const uint64_t op,
1051 const ARN& arn) {
1052 if (!policy)
1053 return Effect::Pass;
1054 else
1055 return policy->eval(env, id, op, arn);
1056 }
1057
1058 }
1059
1060 Effect eval_user_policies(const vector<Policy>& user_policies,
1061 const rgw::IAM::Environment& env,
1062 boost::optional<const rgw::auth::Identity&> id,
1063 const uint64_t op,
1064 const ARN& arn) {
1065 auto usr_policy_res = Effect::Pass, prev_res = Effect::Pass;
1066 for (auto& user_policy : user_policies) {
1067 if (usr_policy_res = eval_or_pass(user_policy, env, id, op, arn); usr_policy_res == Effect::Deny)
1068 return usr_policy_res;
1069 else if (usr_policy_res == Effect::Allow)
1070 prev_res = Effect::Allow;
1071 else if (usr_policy_res == Effect::Pass && prev_res == Effect::Allow)
1072 usr_policy_res = Effect::Allow;
1073 }
1074 return usr_policy_res;
1075 }
1076
1077 bool verify_user_permission(const DoutPrefixProvider* dpp,
1078 perm_state_base * const s,
1079 RGWAccessControlPolicy * const user_acl,
1080 const vector<rgw::IAM::Policy>& user_policies,
1081 const rgw::ARN& res,
1082 const uint64_t op)
1083 {
1084 auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, res);
1085 if (usr_policy_res == Effect::Deny) {
1086 return false;
1087 }
1088
1089 if (usr_policy_res == Effect::Allow) {
1090 return true;
1091 }
1092
1093 if (op == rgw::IAM::s3CreateBucket || op == rgw::IAM::s3ListAllMyBuckets) {
1094 auto perm = op_to_perm(op);
1095
1096 return verify_user_permission_no_policy(dpp, s, user_acl, perm);
1097 }
1098
1099 return false;
1100 }
1101
1102 bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1103 struct perm_state_base * const s,
1104 RGWAccessControlPolicy * const user_acl,
1105 const int perm)
1106 {
1107 if (s->identity->get_identity_type() == TYPE_ROLE)
1108 return false;
1109
1110 /* S3 doesn't support account ACLs. */
1111 if (!user_acl)
1112 return true;
1113
1114 if ((perm & (int)s->perm_mask) != perm)
1115 return false;
1116
1117 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
1118 }
1119
1120 bool verify_user_permission(const DoutPrefixProvider* dpp,
1121 struct req_state * const s,
1122 const rgw::ARN& res,
1123 const uint64_t op)
1124 {
1125 perm_state_from_req_state ps(s);
1126 return verify_user_permission(dpp, &ps, s->user_acl.get(), s->iam_user_policies, res, op);
1127 }
1128
1129 bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1130 struct req_state * const s,
1131 const int perm)
1132 {
1133 perm_state_from_req_state ps(s);
1134 return verify_user_permission_no_policy(dpp, &ps, s->user_acl.get(), perm);
1135 }
1136
1137 bool verify_requester_payer_permission(struct perm_state_base *s)
1138 {
1139 if (!s->bucket_info.requester_pays)
1140 return true;
1141
1142 if (s->identity->is_owner_of(s->bucket_info.owner))
1143 return true;
1144
1145 if (s->identity->is_anonymous()) {
1146 return false;
1147 }
1148
1149 auto request_payer = s->get_request_payer();
1150 if (request_payer) {
1151 return *request_payer;
1152 }
1153
1154 return false;
1155 }
1156
1157 bool verify_bucket_permission(const DoutPrefixProvider* dpp,
1158 struct perm_state_base * const s,
1159 const rgw_bucket& bucket,
1160 RGWAccessControlPolicy * const user_acl,
1161 RGWAccessControlPolicy * const bucket_acl,
1162 const boost::optional<Policy>& bucket_policy,
1163 const vector<Policy>& user_policies,
1164 const uint64_t op)
1165 {
1166 if (!verify_requester_payer_permission(s))
1167 return false;
1168
1169 auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, ARN(bucket));
1170 if (usr_policy_res == Effect::Deny)
1171 return false;
1172
1173 auto r = eval_or_pass(bucket_policy, s->env, *s->identity,
1174 op, ARN(bucket));
1175 if (r == Effect::Allow)
1176 // It looks like S3 ACLs only GRANT permissions rather than
1177 // denying them, so this should be safe.
1178 return true;
1179 else if (r == Effect::Deny)
1180 return false;
1181 else if (usr_policy_res == Effect::Allow) // r is Effect::Pass at this point
1182 return true;
1183
1184 const auto perm = op_to_perm(op);
1185
1186 return verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm);
1187 }
1188
1189 bool verify_bucket_permission(const DoutPrefixProvider* dpp,
1190 struct req_state * const s,
1191 const rgw_bucket& bucket,
1192 RGWAccessControlPolicy * const user_acl,
1193 RGWAccessControlPolicy * const bucket_acl,
1194 const boost::optional<Policy>& bucket_policy,
1195 const vector<Policy>& user_policies,
1196 const uint64_t op)
1197 {
1198 perm_state_from_req_state ps(s);
1199 return verify_bucket_permission(dpp, &ps, bucket,
1200 user_acl, bucket_acl,
1201 bucket_policy, user_policies,
1202 op);
1203 }
1204
1205 bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
1206 RGWAccessControlPolicy * const user_acl,
1207 RGWAccessControlPolicy * const bucket_acl,
1208 const int perm)
1209 {
1210 if (!bucket_acl)
1211 return false;
1212
1213 if ((perm & (int)s->perm_mask) != perm)
1214 return false;
1215
1216 if (bucket_acl->verify_permission(dpp, *s->identity, perm, perm,
1217 s->get_referer(),
1218 s->bucket_access_conf &&
1219 s->bucket_access_conf->ignore_public_acls()))
1220 return true;
1221
1222 if (!user_acl)
1223 return false;
1224
1225 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
1226 }
1227
1228 bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s,
1229 RGWAccessControlPolicy * const user_acl,
1230 RGWAccessControlPolicy * const bucket_acl,
1231 const int perm)
1232 {
1233 perm_state_from_req_state ps(s);
1234 return verify_bucket_permission_no_policy(dpp,
1235 &ps,
1236 user_acl,
1237 bucket_acl,
1238 perm);
1239 }
1240
1241 bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s, const int perm)
1242 {
1243 perm_state_from_req_state ps(s);
1244
1245 if (!verify_requester_payer_permission(&ps))
1246 return false;
1247
1248 return verify_bucket_permission_no_policy(dpp,
1249 &ps,
1250 s->user_acl.get(),
1251 s->bucket_acl.get(),
1252 perm);
1253 }
1254
1255 bool verify_bucket_permission(const DoutPrefixProvider* dpp, struct req_state * const s, const uint64_t op)
1256 {
1257 perm_state_from_req_state ps(s);
1258
1259 return verify_bucket_permission(dpp,
1260 &ps,
1261 s->bucket->get_key(),
1262 s->user_acl.get(),
1263 s->bucket_acl.get(),
1264 s->iam_policy,
1265 s->iam_user_policies,
1266 op);
1267 }
1268
1269 // Authorize anyone permitted by the policy and the bucket owner
1270 // unless explicitly denied by the policy.
1271
1272 int verify_bucket_owner_or_policy(struct req_state* const s,
1273 const uint64_t op)
1274 {
1275 auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, boost::none, op, ARN(s->bucket->get_key()));
1276 if (usr_policy_res == Effect::Deny) {
1277 return -EACCES;
1278 }
1279
1280 auto e = eval_or_pass(s->iam_policy,
1281 s->env, *s->auth.identity,
1282 op, ARN(s->bucket->get_key()));
1283 if (e == Effect::Deny) {
1284 return -EACCES;
1285 }
1286
1287 if (e == Effect::Allow ||
1288 usr_policy_res == Effect::Allow ||
1289 (e == Effect::Pass &&
1290 usr_policy_res == Effect::Pass &&
1291 s->auth.identity->is_owner_of(s->bucket_owner.get_id()))) {
1292 return 0;
1293 } else {
1294 return -EACCES;
1295 }
1296 }
1297
1298
1299 static inline bool check_deferred_bucket_perms(const DoutPrefixProvider* dpp,
1300 struct perm_state_base * const s,
1301 const rgw_bucket& bucket,
1302 RGWAccessControlPolicy * const user_acl,
1303 RGWAccessControlPolicy * const bucket_acl,
1304 const boost::optional<Policy>& bucket_policy,
1305 const vector<Policy>& user_policies,
1306 const uint8_t deferred_check,
1307 const uint64_t op)
1308 {
1309 return (s->defer_to_bucket_acls == deferred_check \
1310 && verify_bucket_permission(dpp, s, bucket, user_acl, bucket_acl, bucket_policy, user_policies,op));
1311 }
1312
1313 static inline bool check_deferred_bucket_only_acl(const DoutPrefixProvider* dpp,
1314 struct perm_state_base * const s,
1315 RGWAccessControlPolicy * const user_acl,
1316 RGWAccessControlPolicy * const bucket_acl,
1317 const uint8_t deferred_check,
1318 const int perm)
1319 {
1320 return (s->defer_to_bucket_acls == deferred_check \
1321 && verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm));
1322 }
1323
1324 bool verify_object_permission(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
1325 const rgw_obj& obj,
1326 RGWAccessControlPolicy * const user_acl,
1327 RGWAccessControlPolicy * const bucket_acl,
1328 RGWAccessControlPolicy * const object_acl,
1329 const boost::optional<Policy>& bucket_policy,
1330 const vector<Policy>& user_policies,
1331 const uint64_t op)
1332 {
1333 if (!verify_requester_payer_permission(s))
1334 return false;
1335
1336 auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, ARN(obj));
1337 if (usr_policy_res == Effect::Deny)
1338 return false;
1339
1340 auto r = eval_or_pass(bucket_policy, s->env, *s->identity, op, ARN(obj));
1341 if (r == Effect::Allow)
1342 // It looks like S3 ACLs only GRANT permissions rather than
1343 // denying them, so this should be safe.
1344 return true;
1345 else if (r == Effect::Deny)
1346 return false;
1347 else if (usr_policy_res == Effect::Allow)
1348 return true;
1349
1350 const auto perm = op_to_perm(op);
1351
1352 if (check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
1353 user_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
1354 check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
1355 user_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
1356 return true;
1357 }
1358
1359 if (!object_acl) {
1360 return false;
1361 }
1362
1363 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1364 nullptr, /* http_referrer */
1365 s->bucket_access_conf &&
1366 s->bucket_access_conf->ignore_public_acls());
1367 if (ret) {
1368 return true;
1369 }
1370
1371 if (!s->cct->_conf->rgw_enforce_swift_acls)
1372 return ret;
1373
1374 if ((perm & (int)s->perm_mask) != perm)
1375 return false;
1376
1377 int swift_perm = 0;
1378 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1379 swift_perm |= RGW_PERM_READ_OBJS;
1380 if (perm & RGW_PERM_WRITE)
1381 swift_perm |= RGW_PERM_WRITE_OBJS;
1382
1383 if (!swift_perm)
1384 return false;
1385
1386 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1387 otherwise the mask might not cover the swift permissions bits */
1388 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1389 s->get_referer()))
1390 return true;
1391
1392 if (!user_acl)
1393 return false;
1394
1395 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
1396 }
1397
1398 bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state * const s,
1399 const rgw_obj& obj,
1400 RGWAccessControlPolicy * const user_acl,
1401 RGWAccessControlPolicy * const bucket_acl,
1402 RGWAccessControlPolicy * const object_acl,
1403 const boost::optional<Policy>& bucket_policy,
1404 const vector<Policy>& user_policies,
1405 const uint64_t op)
1406 {
1407 perm_state_from_req_state ps(s);
1408 return verify_object_permission(dpp, &ps, obj,
1409 user_acl, bucket_acl,
1410 object_acl, bucket_policy,
1411 user_policies, op);
1412 }
1413
1414 bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp,
1415 struct perm_state_base * const s,
1416 RGWAccessControlPolicy * const user_acl,
1417 RGWAccessControlPolicy * const bucket_acl,
1418 RGWAccessControlPolicy * const object_acl,
1419 const int perm)
1420 {
1421 if (check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, perm) ||
1422 check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, RGW_PERM_FULL_CONTROL)) {
1423 return true;
1424 }
1425
1426 if (!object_acl) {
1427 return false;
1428 }
1429
1430 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1431 nullptr, /* http referrer */
1432 s->bucket_access_conf &&
1433 s->bucket_access_conf->ignore_public_acls());
1434 if (ret) {
1435 return true;
1436 }
1437
1438 if (!s->cct->_conf->rgw_enforce_swift_acls)
1439 return ret;
1440
1441 if ((perm & (int)s->perm_mask) != perm)
1442 return false;
1443
1444 int swift_perm = 0;
1445 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1446 swift_perm |= RGW_PERM_READ_OBJS;
1447 if (perm & RGW_PERM_WRITE)
1448 swift_perm |= RGW_PERM_WRITE_OBJS;
1449
1450 if (!swift_perm)
1451 return false;
1452
1453 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1454 otherwise the mask might not cover the swift permissions bits */
1455 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1456 s->get_referer()))
1457 return true;
1458
1459 if (!user_acl)
1460 return false;
1461
1462 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
1463 }
1464
1465 bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state *s, int perm)
1466 {
1467 perm_state_from_req_state ps(s);
1468
1469 if (!verify_requester_payer_permission(&ps))
1470 return false;
1471
1472 return verify_object_permission_no_policy(dpp,
1473 &ps,
1474 s->user_acl.get(),
1475 s->bucket_acl.get(),
1476 s->object_acl.get(),
1477 perm);
1478 }
1479
1480 bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s, uint64_t op)
1481 {
1482 perm_state_from_req_state ps(s);
1483
1484 return verify_object_permission(dpp,
1485 &ps,
1486 rgw_obj(s->bucket->get_key(), s->object->get_key()),
1487 s->user_acl.get(),
1488 s->bucket_acl.get(),
1489 s->object_acl.get(),
1490 s->iam_policy,
1491 s->iam_user_policies,
1492 op);
1493 }
1494
1495
1496 int verify_object_lock(const DoutPrefixProvider* dpp, const rgw::sal::RGWAttrs& attrs, const bool bypass_perm, const bool bypass_governance_mode) {
1497 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
1498 if (aiter != attrs.end()) {
1499 RGWObjectRetention obj_retention;
1500 try {
1501 decode(obj_retention, aiter->second);
1502 } catch (buffer::error& err) {
1503 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
1504 return -EIO;
1505 }
1506 if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) > ceph_clock_now()) {
1507 if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
1508 return -EACCES;
1509 }
1510 }
1511 }
1512 aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD);
1513 if (aiter != attrs.end()) {
1514 RGWObjectLegalHold obj_legal_hold;
1515 try {
1516 decode(obj_legal_hold, aiter->second);
1517 } catch (buffer::error& err) {
1518 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl;
1519 return -EIO;
1520 }
1521 if (obj_legal_hold.is_enabled()) {
1522 return -EACCES;
1523 }
1524 }
1525
1526 return 0;
1527 }
1528
1529
1530 class HexTable
1531 {
1532 char table[256];
1533
1534 public:
1535 HexTable() {
1536 // FIPS zeroization audit 20191115: this memset is not security related.
1537 memset(table, -1, sizeof(table));
1538 int i;
1539 for (i = '0'; i<='9'; i++)
1540 table[i] = i - '0';
1541 for (i = 'A'; i<='F'; i++)
1542 table[i] = i - 'A' + 0xa;
1543 for (i = 'a'; i<='f'; i++)
1544 table[i] = i - 'a' + 0xa;
1545 }
1546
1547 char to_num(char c) {
1548 return table[(int)c];
1549 }
1550 };
1551
1552 static char hex_to_num(char c)
1553 {
1554 static HexTable hex_table;
1555 return hex_table.to_num(c);
1556 }
1557
1558 std::string url_decode(const std::string_view& src_str, bool in_query)
1559 {
1560 std::string dest_str;
1561 dest_str.reserve(src_str.length() + 1);
1562
1563 for (auto src = std::begin(src_str); src != std::end(src_str); ++src) {
1564 if (*src != '%') {
1565 if (!in_query || *src != '+') {
1566 if (*src == '?') {
1567 in_query = true;
1568 }
1569 dest_str.push_back(*src);
1570 } else {
1571 dest_str.push_back(' ');
1572 }
1573 } else {
1574 /* 3 == strlen("%%XX") */
1575 if (std::distance(src, std::end(src_str)) < 3) {
1576 break;
1577 }
1578
1579 src++;
1580 const char c1 = hex_to_num(*src++);
1581 const char c2 = hex_to_num(*src);
1582 if (c1 < 0 || c2 < 0) {
1583 return std::string();
1584 } else {
1585 dest_str.push_back(c1 << 4 | c2);
1586 }
1587 }
1588 }
1589
1590 return dest_str;
1591 }
1592
1593 void rgw_uri_escape_char(char c, string& dst)
1594 {
1595 char buf[16];
1596 snprintf(buf, sizeof(buf), "%%%.2X", (int)(unsigned char)c);
1597 dst.append(buf);
1598 }
1599
1600 static bool char_needs_url_encoding(char c)
1601 {
1602 if (c <= 0x20 || c >= 0x7f)
1603 return true;
1604
1605 switch (c) {
1606 case 0x22:
1607 case 0x23:
1608 case 0x25:
1609 case 0x26:
1610 case 0x2B:
1611 case 0x2C:
1612 case 0x2F:
1613 case 0x3A:
1614 case 0x3B:
1615 case 0x3C:
1616 case 0x3E:
1617 case 0x3D:
1618 case 0x3F:
1619 case 0x40:
1620 case 0x5B:
1621 case 0x5D:
1622 case 0x5C:
1623 case 0x5E:
1624 case 0x60:
1625 case 0x7B:
1626 case 0x7D:
1627 return true;
1628 }
1629 return false;
1630 }
1631
1632 void url_encode(const string& src, string& dst, bool encode_slash)
1633 {
1634 const char *p = src.c_str();
1635 for (unsigned i = 0; i < src.size(); i++, p++) {
1636 if ((!encode_slash && *p == 0x2F) || !char_needs_url_encoding(*p)) {
1637 dst.append(p, 1);
1638 }else {
1639 rgw_uri_escape_char(*p, dst);
1640 }
1641 }
1642 }
1643
1644 std::string url_encode(const std::string& src, bool encode_slash)
1645 {
1646 std::string dst;
1647 url_encode(src, dst, encode_slash);
1648
1649 return dst;
1650 }
1651
1652 std::string url_remove_prefix(const std::string& url)
1653 {
1654 std::string dst = url;
1655 auto pos = dst.find("http://");
1656 if (pos == std::string::npos) {
1657 pos = dst.find("https://");
1658 if (pos != std::string::npos) {
1659 dst.erase(pos, 8);
1660 } else {
1661 pos = dst.find("www.");
1662 if (pos != std::string::npos) {
1663 dst.erase(pos, 4);
1664 }
1665 }
1666 } else {
1667 dst.erase(pos, 7);
1668 }
1669
1670 return dst;
1671 }
1672
1673 string rgw_trim_whitespace(const string& src)
1674 {
1675 if (src.empty()) {
1676 return string();
1677 }
1678
1679 int start = 0;
1680 for (; start != (int)src.size(); start++) {
1681 if (!isspace(src[start]))
1682 break;
1683 }
1684
1685 int end = src.size() - 1;
1686 if (end < start) {
1687 return string();
1688 }
1689
1690 for (; end > start; end--) {
1691 if (!isspace(src[end]))
1692 break;
1693 }
1694
1695 return src.substr(start, end - start + 1);
1696 }
1697
1698 std::string_view rgw_trim_whitespace(const std::string_view& src)
1699 {
1700 std::string_view res = src;
1701
1702 while (res.size() > 0 && std::isspace(res.front())) {
1703 res.remove_prefix(1);
1704 }
1705 while (res.size() > 0 && std::isspace(res.back())) {
1706 res.remove_suffix(1);
1707 }
1708 return res;
1709 }
1710
1711 string rgw_trim_quotes(const string& val)
1712 {
1713 string s = rgw_trim_whitespace(val);
1714 if (s.size() < 2)
1715 return s;
1716
1717 int start = 0;
1718 int end = s.size() - 1;
1719 int quotes_count = 0;
1720
1721 if (s[start] == '"') {
1722 start++;
1723 quotes_count++;
1724 }
1725 if (s[end] == '"') {
1726 end--;
1727 quotes_count++;
1728 }
1729 if (quotes_count == 2) {
1730 return s.substr(start, end - start + 1);
1731 }
1732 return s;
1733 }
1734
1735 static struct rgw_name_to_flag cap_names[] = { {"*", RGW_CAP_ALL},
1736 {"read", RGW_CAP_READ},
1737 {"write", RGW_CAP_WRITE},
1738 {NULL, 0} };
1739
1740 int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
1741 {
1742 return rgw_parse_list_of_flags(cap_names, str, perm);
1743 }
1744
1745 int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm)
1746 {
1747 int pos = cap.find('=');
1748 if (pos >= 0) {
1749 type = rgw_trim_whitespace(cap.substr(0, pos));
1750 }
1751
1752 if (!is_valid_cap_type(type))
1753 return -ERR_INVALID_CAP;
1754
1755 string cap_perm;
1756 uint32_t perm = 0;
1757 if (pos < (int)cap.size() - 1) {
1758 cap_perm = cap.substr(pos + 1);
1759 int r = RGWUserCaps::parse_cap_perm(cap_perm, &perm);
1760 if (r < 0)
1761 return r;
1762 }
1763
1764 *pperm = perm;
1765
1766 return 0;
1767 }
1768
1769 int RGWUserCaps::add_cap(const string& cap)
1770 {
1771 uint32_t perm;
1772 string type;
1773
1774 int r = get_cap(cap, type, &perm);
1775 if (r < 0)
1776 return r;
1777
1778 caps[type] |= perm;
1779
1780 return 0;
1781 }
1782
1783 int RGWUserCaps::remove_cap(const string& cap)
1784 {
1785 uint32_t perm;
1786 string type;
1787
1788 int r = get_cap(cap, type, &perm);
1789 if (r < 0)
1790 return r;
1791
1792 map<string, uint32_t>::iterator iter = caps.find(type);
1793 if (iter == caps.end())
1794 return 0;
1795
1796 uint32_t& old_perm = iter->second;
1797 old_perm &= ~perm;
1798 if (!old_perm)
1799 caps.erase(iter);
1800
1801 return 0;
1802 }
1803
1804 int RGWUserCaps::add_from_string(const string& str)
1805 {
1806 int start = 0;
1807 do {
1808 auto end = str.find(';', start);
1809 if (end == string::npos)
1810 end = str.size();
1811
1812 int r = add_cap(str.substr(start, end - start));
1813 if (r < 0)
1814 return r;
1815
1816 start = end + 1;
1817 } while (start < (int)str.size());
1818
1819 return 0;
1820 }
1821
1822 int RGWUserCaps::remove_from_string(const string& str)
1823 {
1824 int start = 0;
1825 do {
1826 auto end = str.find(';', start);
1827 if (end == string::npos)
1828 end = str.size();
1829
1830 int r = remove_cap(str.substr(start, end - start));
1831 if (r < 0)
1832 return r;
1833
1834 start = end + 1;
1835 } while (start < (int)str.size());
1836
1837 return 0;
1838 }
1839
1840 void RGWUserCaps::dump(Formatter *f) const
1841 {
1842 dump(f, "caps");
1843 }
1844
1845 void RGWUserCaps::dump(Formatter *f, const char *name) const
1846 {
1847 f->open_array_section(name);
1848 map<string, uint32_t>::const_iterator iter;
1849 for (iter = caps.begin(); iter != caps.end(); ++iter)
1850 {
1851 f->open_object_section("cap");
1852 f->dump_string("type", iter->first);
1853 uint32_t perm = iter->second;
1854 string perm_str;
1855 for (int i=0; cap_names[i].type_name; i++) {
1856 if ((perm & cap_names[i].flag) == cap_names[i].flag) {
1857 if (perm_str.size())
1858 perm_str.append(", ");
1859
1860 perm_str.append(cap_names[i].type_name);
1861 perm &= ~cap_names[i].flag;
1862 }
1863 }
1864 if (perm_str.empty())
1865 perm_str = "<none>";
1866
1867 f->dump_string("perm", perm_str);
1868 f->close_section();
1869 }
1870
1871 f->close_section();
1872 }
1873
1874 struct RGWUserCap {
1875 string type;
1876 uint32_t perm;
1877
1878 void decode_json(JSONObj *obj) {
1879 JSONDecoder::decode_json("type", type, obj);
1880 string perm_str;
1881 JSONDecoder::decode_json("perm", perm_str, obj);
1882 if (RGWUserCaps::parse_cap_perm(perm_str, &perm) < 0) {
1883 throw JSONDecoder::err("failed to parse permissions");
1884 }
1885 }
1886 };
1887
1888 void RGWUserCaps::decode_json(JSONObj *obj)
1889 {
1890 list<RGWUserCap> caps_list;
1891 decode_json_obj(caps_list, obj);
1892
1893 list<RGWUserCap>::iterator iter;
1894 for (iter = caps_list.begin(); iter != caps_list.end(); ++iter) {
1895 RGWUserCap& cap = *iter;
1896 caps[cap.type] = cap.perm;
1897 }
1898 }
1899
1900 int RGWUserCaps::check_cap(const string& cap, uint32_t perm) const
1901 {
1902 auto iter = caps.find(cap);
1903
1904 if ((iter == caps.end()) ||
1905 (iter->second & perm) != perm) {
1906 return -EPERM;
1907 }
1908
1909 return 0;
1910 }
1911
1912 bool RGWUserCaps::is_valid_cap_type(const string& tp)
1913 {
1914 static const char *cap_type[] = { "user",
1915 "users",
1916 "buckets",
1917 "metadata",
1918 "usage",
1919 "zone",
1920 "bilog",
1921 "mdlog",
1922 "datalog",
1923 "roles",
1924 "user-policy",
1925 "amz-cache",
1926 "oidc-provider"};
1927
1928 for (unsigned int i = 0; i < sizeof(cap_type) / sizeof(char *); ++i) {
1929 if (tp.compare(cap_type[i]) == 0) {
1930 return true;
1931 }
1932 }
1933
1934 return false;
1935 }
1936
1937 void rgw_pool::from_str(const string& s)
1938 {
1939 size_t pos = rgw_unescape_str(s, 0, '\\', ':', &name);
1940 if (pos != string::npos) {
1941 pos = rgw_unescape_str(s, pos, '\\', ':', &ns);
1942 /* ignore return; if pos != string::npos it means that we had a colon
1943 * in the middle of ns that wasn't escaped, we're going to stop there
1944 */
1945 }
1946 }
1947
1948 string rgw_pool::to_str() const
1949 {
1950 string esc_name;
1951 rgw_escape_str(name, '\\', ':', &esc_name);
1952 if (ns.empty()) {
1953 return esc_name;
1954 }
1955 string esc_ns;
1956 rgw_escape_str(ns, '\\', ':', &esc_ns);
1957 return esc_name + ":" + esc_ns;
1958 }
1959
1960 void rgw_raw_obj::decode_from_rgw_obj(bufferlist::const_iterator& bl)
1961 {
1962 using ceph::decode;
1963 rgw_obj old_obj;
1964 decode(old_obj, bl);
1965
1966 get_obj_bucket_and_oid_loc(old_obj, oid, loc);
1967 pool = old_obj.get_explicit_data_pool();
1968 }
1969
1970 static struct rgw_name_to_flag op_type_mapping[] = { {"*", RGW_OP_TYPE_ALL},
1971 {"read", RGW_OP_TYPE_READ},
1972 {"write", RGW_OP_TYPE_WRITE},
1973 {"delete", RGW_OP_TYPE_DELETE},
1974 {NULL, 0} };
1975
1976
1977 int rgw_parse_op_type_list(const string& str, uint32_t *perm)
1978 {
1979 return rgw_parse_list_of_flags(op_type_mapping, str, perm);
1980 }
1981
1982 bool match_policy(std::string_view pattern, std::string_view input,
1983 uint32_t flag)
1984 {
1985 const uint32_t flag2 = flag & (MATCH_POLICY_ACTION|MATCH_POLICY_ARN) ?
1986 MATCH_CASE_INSENSITIVE : 0;
1987 const bool colonblocks = !(flag & (MATCH_POLICY_RESOURCE |
1988 MATCH_POLICY_STRING));
1989
1990 const auto npos = std::string_view::npos;
1991 std::string_view::size_type last_pos_input = 0, last_pos_pattern = 0;
1992 while (true) {
1993 auto cur_pos_input = colonblocks ? input.find(":", last_pos_input) : npos;
1994 auto cur_pos_pattern =
1995 colonblocks ? pattern.find(":", last_pos_pattern) : npos;
1996
1997 auto substr_input = input.substr(last_pos_input, cur_pos_input);
1998 auto substr_pattern = pattern.substr(last_pos_pattern, cur_pos_pattern);
1999
2000 if (!match_wildcards(substr_pattern, substr_input, flag2))
2001 return false;
2002
2003 if (cur_pos_pattern == npos)
2004 return cur_pos_input == npos;
2005 if (cur_pos_input == npos)
2006 return false;
2007
2008 last_pos_pattern = cur_pos_pattern + 1;
2009 last_pos_input = cur_pos_input + 1;
2010 }
2011 }
2012
2013 /*
2014 * make attrs look-like-this
2015 * converts underscores to dashes
2016 */
2017 string lowercase_dash_http_attr(const string& orig)
2018 {
2019 const char *s = orig.c_str();
2020 char buf[orig.size() + 1];
2021 buf[orig.size()] = '\0';
2022
2023 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2024 switch (*s) {
2025 case '_':
2026 buf[i] = '-';
2027 break;
2028 default:
2029 buf[i] = tolower(*s);
2030 }
2031 }
2032 return string(buf);
2033 }
2034
2035 /*
2036 * make attrs Look-Like-This
2037 * converts underscores to dashes
2038 */
2039 string camelcase_dash_http_attr(const string& orig)
2040 {
2041 const char *s = orig.c_str();
2042 char buf[orig.size() + 1];
2043 buf[orig.size()] = '\0';
2044
2045 bool last_sep = true;
2046
2047 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2048 switch (*s) {
2049 case '_':
2050 case '-':
2051 buf[i] = '-';
2052 last_sep = true;
2053 break;
2054 default:
2055 if (last_sep) {
2056 buf[i] = toupper(*s);
2057 } else {
2058 buf[i] = tolower(*s);
2059 }
2060 last_sep = false;
2061 }
2062 }
2063 return string(buf);
2064 }
2065
2066 RGWBucketInfo::RGWBucketInfo()
2067 {
2068 }
2069
2070 RGWBucketInfo::~RGWBucketInfo()
2071 {
2072 }
2073
2074 void RGWBucketInfo::encode(bufferlist& bl) const {
2075 ENCODE_START(23, 4, bl);
2076 encode(bucket, bl);
2077 encode(owner.id, bl);
2078 encode(flags, bl);
2079 encode(zonegroup, bl);
2080 uint64_t ct = real_clock::to_time_t(creation_time);
2081 encode(ct, bl);
2082 encode(placement_rule, bl);
2083 encode(has_instance_obj, bl);
2084 encode(quota, bl);
2085 encode(requester_pays, bl);
2086 encode(owner.tenant, bl);
2087 encode(has_website, bl);
2088 if (has_website) {
2089 encode(website_conf, bl);
2090 }
2091 encode(swift_versioning, bl);
2092 if (swift_versioning) {
2093 encode(swift_ver_location, bl);
2094 }
2095 encode(creation_time, bl);
2096 encode(mdsearch_config, bl);
2097 encode(reshard_status, bl);
2098 encode(new_bucket_instance_id, bl);
2099 if (obj_lock_enabled()) {
2100 encode(obj_lock, bl);
2101 }
2102 bool has_sync_policy = !empty_sync_policy();
2103 encode(has_sync_policy, bl);
2104 if (has_sync_policy) {
2105 encode(*sync_policy, bl);
2106 }
2107 encode(layout, bl);
2108 encode(owner.ns, bl);
2109 ENCODE_FINISH(bl);
2110 }
2111
2112 void RGWBucketInfo::decode(bufferlist::const_iterator& bl) {
2113 DECODE_START_LEGACY_COMPAT_LEN_32(23, 4, 4, bl);
2114 decode(bucket, bl);
2115 if (struct_v >= 2) {
2116 string s;
2117 decode(s, bl);
2118 owner.from_str(s);
2119 }
2120 if (struct_v >= 3)
2121 decode(flags, bl);
2122 if (struct_v >= 5)
2123 decode(zonegroup, bl);
2124 if (struct_v >= 6) {
2125 uint64_t ct;
2126 decode(ct, bl);
2127 if (struct_v < 17)
2128 creation_time = ceph::real_clock::from_time_t((time_t)ct);
2129 }
2130 if (struct_v >= 7)
2131 decode(placement_rule, bl);
2132 if (struct_v >= 8)
2133 decode(has_instance_obj, bl);
2134 if (struct_v >= 9)
2135 decode(quota, bl);
2136 static constexpr uint8_t new_layout_v = 22;
2137 if (struct_v >= 10 && struct_v < new_layout_v)
2138 decode(layout.current_index.layout.normal.num_shards, bl);
2139 if (struct_v >= 11 && struct_v < new_layout_v)
2140 decode(layout.current_index.layout.normal.hash_type, bl);
2141 if (struct_v >= 12)
2142 decode(requester_pays, bl);
2143 if (struct_v >= 13)
2144 decode(owner.tenant, bl);
2145 if (struct_v >= 14) {
2146 decode(has_website, bl);
2147 if (has_website) {
2148 decode(website_conf, bl);
2149 } else {
2150 website_conf = RGWBucketWebsiteConf();
2151 }
2152 }
2153 if (struct_v >= 15 && struct_v < new_layout_v) {
2154 uint32_t it;
2155 decode(it, bl);
2156 layout.current_index.layout.type = (rgw::BucketIndexType)it;
2157 } else {
2158 layout.current_index.layout.type = rgw::BucketIndexType::Normal;
2159 }
2160 swift_versioning = false;
2161 swift_ver_location.clear();
2162 if (struct_v >= 16) {
2163 decode(swift_versioning, bl);
2164 if (swift_versioning) {
2165 decode(swift_ver_location, bl);
2166 }
2167 }
2168 if (struct_v >= 17) {
2169 decode(creation_time, bl);
2170 }
2171 if (struct_v >= 18) {
2172 decode(mdsearch_config, bl);
2173 }
2174 if (struct_v >= 19) {
2175 decode(reshard_status, bl);
2176 decode(new_bucket_instance_id, bl);
2177 }
2178 if (struct_v >= 20 && obj_lock_enabled()) {
2179 decode(obj_lock, bl);
2180 }
2181 if (struct_v >= 21) {
2182 decode(sync_policy, bl);
2183 }
2184 if (struct_v >= 22) {
2185 decode(layout, bl);
2186 }
2187 if (struct_v >= 23) {
2188 decode(owner.ns, bl);
2189 }
2190 DECODE_FINISH(bl);
2191 }
2192
2193 void RGWBucketInfo::set_sync_policy(rgw_sync_policy_info&& policy)
2194 {
2195 sync_policy = std::move(policy);
2196 }
2197
2198 bool RGWBucketInfo::empty_sync_policy() const
2199 {
2200 if (!sync_policy) {
2201 return true;
2202 }
2203
2204 return sync_policy->empty();
2205 }
2206