]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_common.cc
import ceph quincy 17.2.4
[ceph.git] / ceph / src / rgw / rgw_common.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 <vector>
6#include <algorithm>
7#include <string>
8#include <boost/tokenizer.hpp>
7c673cae
FG
9
10#include "json_spirit/json_spirit.h"
11#include "common/ceph_json.h"
20effc67 12#include "common/Formatter.h"
7c673cae 13
31f18b77 14#include "rgw_op.h"
7c673cae
FG
15#include "rgw_common.h"
16#include "rgw_acl.h"
17#include "rgw_string.h"
31f18b77 18#include "rgw_http_errors.h"
eafe8130 19#include "rgw_arn.h"
9f95a23c 20#include "rgw_data_sync.h"
7c673cae
FG
21
22#include "common/ceph_crypto.h"
23#include "common/armor.h"
24#include "common/errno.h"
25#include "common/Clock.h"
11fdf7f2 26#include "common/convenience.h"
7c673cae
FG
27#include "common/strtol.h"
28#include "include/str_list.h"
7c673cae 29#include "rgw_crypt_sanitize.h"
9f95a23c
TL
30#include "rgw_bucket_sync.h"
31#include "rgw_sync_policy.h"
32
33#include "services/svc_zone.h"
7c673cae
FG
34
35#include <sstream>
36
37#define dout_context g_ceph_context
38#define dout_subsys ceph_subsys_rgw
39
eafe8130 40using rgw::ARN;
31f18b77
FG
41using rgw::IAM::Effect;
42using rgw::IAM::op_to_perm;
43using rgw::IAM::Policy;
7c673cae 44
7c673cae
FG
45const uint32_t RGWBucketInfo::NUM_SHARDS_BLIND_BUCKET(UINT32_MAX);
46
31f18b77
FG
47rgw_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" }},
c07f9fc5 62 { ERR_ZONEGROUP_DEFAULT_PLACEMENT_MISCONFIGURATION, {400, "ZonegroupDefaultPlacementMisconfiguration" }},
31f18b77
FG
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" }},
11fdf7f2 74 { ERR_MALFORMED_DOC, {400, "MalformedPolicyDocument"}},
224ce89b 75 { ERR_INVALID_TAG, {400, "InvalidTag"}},
c07f9fc5 76 { ERR_MALFORMED_ACL_ERROR, {400, "MalformedACLError" }},
11fdf7f2
TL
77 { ERR_INVALID_CORS_RULES_ERROR, {400, "InvalidRequest" }},
78 { ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR, {400, "InvalidRequest" }},
3efd9988 79 { ERR_INVALID_ENCRYPTION_ALGORITHM, {400, "InvalidEncryptionAlgorithmError" }},
eafe8130 80 { ERR_INVALID_RETENTION_PERIOD,{400, "InvalidRetentionPeriod"}},
9f95a23c 81 { ERR_LIMIT_EXCEEDED, {400, "LimitExceeded" }},
31f18b77
FG
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" }},
11fdf7f2 90 { ERR_MFA_REQUIRED, {403, "AccessDenied" }},
31f18b77
FG
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"}},
224ce89b 98 { ERR_NO_SUCH_USER, {404, "NoSuchUser"}},
11fdf7f2
TL
99 { ERR_NO_ROLE_FOUND, {404, "NoSuchEntity"}},
100 { ERR_NO_CORS_FOUND, {404, "NoSuchCORSConfiguration"}},
224ce89b 101 { ERR_NO_SUCH_SUBUSER, {404, "NoSuchSubUser"}},
11fdf7f2 102 { ERR_NO_SUCH_ENTITY, {404, "NoSuchEntity"}},
3a9019d9 103 { ERR_NO_SUCH_CORS_CONFIGURATION, {404, "NoSuchCORSConfiguration"}},
eafe8130 104 { ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION, {404, "ObjectLockConfigurationNotFoundError"}},
31f18b77
FG
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"}},
224ce89b 111 { ERR_TAG_CONFLICT, {409, "OperationAborted"}},
11fdf7f2
TL
112 { ERR_POSITION_NOT_EQUAL_TO_LENGTH, {409, "PositionNotEqualToLength"}},
113 { ERR_OBJECT_NOT_APPENDABLE, {409, "ObjectNotAppendable"}},
114 { ERR_INVALID_BUCKET_STATE, {409, "InvalidBucketState"}},
20effc67 115 { ERR_INVALID_OBJECT_STATE, {403, "InvalidObjectState"}},
31f18b77
FG
116 { ERR_INVALID_SECRET_KEY, {400, "InvalidSecretKey"}},
117 { ERR_INVALID_KEY_TYPE, {400, "InvalidKeyType"}},
118 { ERR_INVALID_CAP, {400, "InvalidCapability"}},
119 { ERR_INVALID_TENANT_NAME, {400, "InvalidTenantName" }},
120 { ENOTEMPTY, {409, "BucketNotEmpty" }},
121 { ERR_PRECONDITION_FAILED, {412, "PreconditionFailed" }},
122 { ERANGE, {416, "InvalidRange" }},
123 { ERR_UNPROCESSABLE_ENTITY, {422, "UnprocessableEntity" }},
124 { ERR_LOCKED, {423, "Locked" }},
125 { ERR_INTERNAL_ERROR, {500, "InternalError" }},
126 { ERR_NOT_IMPLEMENTED, {501, "NotImplemented" }},
127 { ERR_SERVICE_UNAVAILABLE, {503, "ServiceUnavailable"}},
11fdf7f2 128 { ERR_RATE_LIMITED, {503, "SlowDown"}},
224ce89b 129 { ERR_ZERO_IN_URL, {400, "InvalidRequest" }},
9f95a23c 130 { ERR_NO_SUCH_TAG_SET, {404, "NoSuchTagSetError"}},
20effc67 131 { ERR_NO_SUCH_BUCKET_ENCRYPTION_CONFIGURATION, {404, "ServerSideEncryptionConfigurationNotFoundError"}},
31f18b77
FG
132});
133
134rgw_http_errors rgw_http_swift_errors({
135 { EACCES, {403, "AccessDenied" }},
136 { EPERM, {401, "AccessDenied" }},
3efd9988 137 { ENAMETOOLONG, {400, "Metadata name too long" }},
31f18b77
FG
138 { ERR_USER_SUSPENDED, {401, "UserSuspended" }},
139 { ERR_INVALID_UTF8, {412, "Invalid UTF8" }},
140 { ERR_BAD_URL, {412, "Bad URL" }},
141 { ERR_NOT_SLO_MANIFEST, {400, "Not an SLO manifest" }},
142 { ERR_QUOTA_EXCEEDED, {413, "QuotaExceeded" }},
3efd9988
FG
143 { ENOTEMPTY, {409, "There was a conflict when trying "
144 "to complete your request." }},
224ce89b
WB
145 /* FIXME(rzarzynski): we need to find a way to apply Swift's error handling
146 * procedures also for ERR_ZERO_IN_URL. This make a problem as the validation
147 * is performed very early, even before setting the req_state::proto_flags. */
148 { ERR_ZERO_IN_URL, {412, "Invalid UTF8 or contains NULL"}},
11fdf7f2 149 { ERR_RATE_LIMITED, {498, "Rate Limited"}},
31f18b77
FG
150});
151
11fdf7f2
TL
152rgw_http_errors rgw_http_sts_errors({
153 { ERR_PACKED_POLICY_TOO_LARGE, {400, "PackedPolicyTooLarge" }},
154 { ERR_INVALID_IDENTITY_TOKEN, {400, "InvalidIdentityToken" }},
155});
7c673cae 156
92f5a8d4 157rgw_http_errors rgw_http_iam_errors({
f91f0fd5
TL
158 { EINVAL, {400, "InvalidInput" }},
159 { ENOENT, {404, "NoSuchEntity"}},
92f5a8d4
TL
160 { ERR_ROLE_EXISTS, {409, "EntityAlreadyExists"}},
161 { ERR_DELETE_CONFLICT, {409, "DeleteConflict"}},
f91f0fd5
TL
162 { EEXIST, {409, "EntityAlreadyExists"}},
163 { ERR_INTERNAL_ERROR, {500, "ServiceFailure" }},
92f5a8d4
TL
164});
165
20effc67 166using namespace std;
7c673cae
FG
167using namespace ceph::crypto;
168
169rgw_err::
170rgw_err()
171{
172 clear();
173}
174
7c673cae
FG
175void rgw_err::
176clear()
177{
178 http_ret = 200;
179 ret = 0;
31f18b77 180 err_code.clear();
7c673cae
FG
181}
182
183bool rgw_err::
184is_clear() const
185{
186 return (http_ret == 200);
187}
188
189bool rgw_err::
190is_err() const
191{
192 return !(http_ret >= 200 && http_ret <= 399);
193}
194
195// The requestURI transferred from the frontend can be abs_path or absoluteURI
196// If it is absoluteURI, we should adjust it to abs_path for the following
197// S3 authorization and some other processes depending on the requestURI
198// The absoluteURI can start with "http://", "https://", "ws://" or "wss://"
199static string get_abs_path(const string& request_uri) {
200 const static string ABS_PREFIXS[] = {"http://", "https://", "ws://", "wss://"};
201 bool isAbs = false;
202 for (int i = 0; i < 4; ++i) {
203 if (boost::algorithm::starts_with(request_uri, ABS_PREFIXS[i])) {
204 isAbs = true;
205 break;
206 }
207 }
208 if (!isAbs) { // it is not a valid absolute uri
209 return request_uri;
210 }
211 size_t beg_pos = request_uri.find("://") + 3;
212 size_t len = request_uri.size();
213 beg_pos = request_uri.find('/', beg_pos);
214 if (beg_pos == string::npos) return request_uri;
215 return request_uri.substr(beg_pos, len - beg_pos);
216}
217
31f18b77 218req_info::req_info(CephContext *cct, const class RGWEnv *env) : env(env) {
7c673cae
FG
219 method = env->get("REQUEST_METHOD", "");
220 script_uri = env->get("SCRIPT_URI", cct->_conf->rgw_script_uri.c_str());
221 request_uri = env->get("REQUEST_URI", cct->_conf->rgw_request_uri.c_str());
222 if (request_uri[0] != '/') {
223 request_uri = get_abs_path(request_uri);
224 }
31f18b77
FG
225 auto pos = request_uri.find('?');
226 if (pos != string::npos) {
7c673cae
FG
227 request_params = request_uri.substr(pos + 1);
228 request_uri = request_uri.substr(0, pos);
229 } else {
230 request_params = env->get("QUERY_STRING", "");
231 }
232 host = env->get("HTTP_HOST", "");
233
234 // strip off any trailing :port from host (added by CrossFTP and maybe others)
235 size_t colon_offset = host.find_last_of(':');
236 if (colon_offset != string::npos) {
237 bool all_digits = true;
238 for (unsigned i = colon_offset + 1; i < host.size(); ++i) {
239 if (!isdigit(host[i])) {
240 all_digits = false;
241 break;
242 }
243 }
244 if (all_digits) {
245 host.resize(colon_offset);
246 }
247 }
248}
249
250void req_info::rebuild_from(req_info& src)
251{
252 method = src.method;
253 script_uri = src.script_uri;
254 args = src.args;
255 if (src.effective_uri.empty()) {
256 request_uri = src.request_uri;
257 } else {
258 request_uri = src.effective_uri;
259 }
260 effective_uri.clear();
261 host = src.host;
262
263 x_meta_map = src.x_meta_map;
264 x_meta_map.erase("x-amz-date");
265}
266
267
f67539c2
TL
268req_state::req_state(CephContext* _cct, RGWEnv* e, uint64_t id)
269 : cct(_cct), info(_cct, e), id(id)
7c673cae 270{
11fdf7f2
TL
271 enable_ops_log = e->get_enable_ops_log();
272 enable_usage_log = e->get_enable_usage_log();
273 defer_to_bucket_acls = e->get_defer_to_bucket_acls();
7c673cae 274
11fdf7f2 275 time = Clock::now();
7c673cae
FG
276}
277
278req_state::~req_state() {
279 delete formatter;
7c673cae
FG
280}
281
11fdf7f2
TL
282std::ostream& req_state::gen_prefix(std::ostream& out) const
283{
284 auto p = out.precision();
285 return out << "req " << id << ' '
286 << std::setprecision(3) << std::fixed << time_elapsed() // '0.123s'
287 << std::setprecision(p) << std::defaultfloat << ' ';
288}
289
290bool search_err(rgw_http_errors& errs, int err_no, int& http_ret, string& code)
31f18b77
FG
291{
292 auto r = errs.find(err_no);
293 if (r != errs.end()) {
11fdf7f2
TL
294 http_ret = r->second.first;
295 code = r->second.second;
296 return true;
31f18b77
FG
297 }
298 return false;
299}
300
301void set_req_state_err(struct rgw_err& err, /* out */
302 int err_no, /* in */
303 const int prot_flags) /* in */
304{
305 if (err_no < 0)
306 err_no = -err_no;
307
308 err.ret = -err_no;
31f18b77
FG
309
310 if (prot_flags & RGW_REST_SWIFT) {
11fdf7f2
TL
311 if (search_err(rgw_http_swift_errors, err_no, err.http_ret, err.err_code))
312 return;
313 }
314
315 if (prot_flags & RGW_REST_STS) {
316 if (search_err(rgw_http_sts_errors, err_no, err.http_ret, err.err_code))
31f18b77
FG
317 return;
318 }
319
92f5a8d4
TL
320 if (prot_flags & RGW_REST_IAM) {
321 if (search_err(rgw_http_iam_errors, err_no, err.http_ret, err.err_code))
322 return;
323 }
324
31f18b77 325 //Default to searching in s3 errors
11fdf7f2 326 if (search_err(rgw_http_s3_errors, err_no, err.http_ret, err.err_code))
31f18b77
FG
327 return;
328 dout(0) << "WARNING: set_req_state_err err_no=" << err_no
329 << " resorting to 500" << dendl;
330
331 err.http_ret = 500;
332 err.err_code = "UnknownError";
333}
334
335void set_req_state_err(struct req_state* s, int err_no, const string& err_msg)
336{
337 if (s) {
338 set_req_state_err(s, err_no);
3efd9988
FG
339 if (s->prot_flags & RGW_REST_SWIFT && !err_msg.empty()) {
340 /* TODO(rzarzynski): there never ever should be a check like this one.
341 * It's here only for the sake of the patch's backportability. Further
342 * commits will move the logic to a per-RGWHandler replacement of
343 * the end_header() function. Alternativaly, we might consider making
344 * that just for the dump(). Please take a look on @cbodley's comments
345 * in PR #10690 (https://github.com/ceph/ceph/pull/10690). */
346 s->err.err_code = err_msg;
347 } else {
348 s->err.message = err_msg;
349 }
31f18b77
FG
350 }
351}
352
353void set_req_state_err(struct req_state* s, int err_no)
354{
355 if (s) {
356 set_req_state_err(s->err, err_no, s->prot_flags);
357 }
358}
359
360void dump(struct req_state* s)
361{
362 if (s->format != RGW_FORMAT_HTML)
363 s->formatter->open_object_section("Error");
364 if (!s->err.err_code.empty())
365 s->formatter->dump_string("Code", s->err.err_code);
366 if (!s->err.message.empty())
367 s->formatter->dump_string("Message", s->err.message);
368 if (!s->bucket_name.empty()) // TODO: connect to expose_bucket
369 s->formatter->dump_string("BucketName", s->bucket_name);
370 if (!s->trans_id.empty()) // TODO: connect to expose_bucket or another toggle
371 s->formatter->dump_string("RequestId", s->trans_id);
372 s->formatter->dump_string("HostId", s->host_id);
373 if (s->format != RGW_FORMAT_HTML)
374 s->formatter->close_section();
375}
376
7c673cae
FG
377struct str_len {
378 const char *str;
379 int len;
380};
381
382#define STR_LEN_ENTRY(s) { s, sizeof(s) - 1 }
383
384struct str_len meta_prefixes[] = { STR_LEN_ENTRY("HTTP_X_AMZ"),
385 STR_LEN_ENTRY("HTTP_X_GOOG"),
386 STR_LEN_ENTRY("HTTP_X_DHO"),
387 STR_LEN_ENTRY("HTTP_X_RGW"),
388 STR_LEN_ENTRY("HTTP_X_OBJECT"),
389 STR_LEN_ENTRY("HTTP_X_CONTAINER"),
390 STR_LEN_ENTRY("HTTP_X_ACCOUNT"),
391 {NULL, 0} };
392
b3b6e05e 393void req_info::init_meta_info(const DoutPrefixProvider *dpp, bool *found_bad_meta)
7c673cae
FG
394{
395 x_meta_map.clear();
2a845540 396 crypt_attribute_map.clear();
7c673cae 397
31f18b77 398 for (const auto& kv: env->get_map()) {
7c673cae 399 const char *prefix;
31f18b77
FG
400 const string& header_name = kv.first;
401 const string& val = kv.second;
7c673cae
FG
402 for (int prefix_num = 0; (prefix = meta_prefixes[prefix_num].str) != NULL; prefix_num++) {
403 int len = meta_prefixes[prefix_num].len;
404 const char *p = header_name.c_str();
405 if (strncmp(p, prefix, len) == 0) {
b3b6e05e 406 ldpp_dout(dpp, 10) << "meta>> " << p << dendl;
7c673cae
FG
407 const char *name = p+len; /* skip the prefix */
408 int name_len = header_name.size() - len;
409
410 if (found_bad_meta && strncmp(name, "_META_", name_len) == 0)
411 *found_bad_meta = true;
412
413 char name_low[meta_prefixes[0].len + name_len + 1];
414 snprintf(name_low, meta_prefixes[0].len - 5 + name_len + 1, "%s%s", meta_prefixes[0].str + 5 /* skip HTTP_ */, name); // normalize meta prefix
415 int j;
416 for (j = 0; name_low[j]; j++) {
20effc67 417 if (name_low[j] == '_')
7c673cae 418 name_low[j] = '-';
20effc67
TL
419 else if (name_low[j] == '-')
420 name_low[j] = '_';
421 else
422 name_low[j] = tolower(name_low[j]);
7c673cae
FG
423 }
424 name_low[j] = 0;
425
31f18b77
FG
426 auto it = x_meta_map.find(name_low);
427 if (it != x_meta_map.end()) {
428 string old = it->second;
429 boost::algorithm::trim_right(old);
7c673cae
FG
430 old.append(",");
431 old.append(val);
432 x_meta_map[name_low] = old;
433 } else {
434 x_meta_map[name_low] = val;
435 }
2a845540
TL
436 if (strncmp(name_low, "x-amz-server-side-encryption", 20) == 0) {
437 crypt_attribute_map[name_low] = val;
438 }
7c673cae
FG
439 }
440 }
441 }
31f18b77 442 for (const auto& kv: x_meta_map) {
b3b6e05e 443 ldpp_dout(dpp, 10) << "x>> " << kv.first << ":" << rgw::crypt_sanitize::x_meta_map{kv.first, kv.second} << dendl;
7c673cae
FG
444 }
445}
446
447std::ostream& operator<<(std::ostream& oss, const rgw_err &err)
448{
31f18b77 449 oss << "rgw_err(http_ret=" << err.http_ret << ", err_code='" << err.err_code << "') ";
7c673cae
FG
450 return oss;
451}
452
9f95a23c
TL
453void rgw_add_amz_meta_header(
454 meta_map_t& x_meta_map,
455 const std::string& k,
456 const std::string& v)
457{
458 auto it = x_meta_map.find(k);
459 if (it != x_meta_map.end()) {
460 std::string old = it->second;
461 boost::algorithm::trim_right(old);
462 old.append(",");
463 old.append(v);
464 x_meta_map[k] = old;
465 } else {
466 x_meta_map[k] = v;
467 }
468}
469
2a845540
TL
470bool rgw_set_amz_meta_header(
471 meta_map_t& x_meta_map,
472 const std::string& k,
473 const std::string& v,
474 rgw_set_action_if_set a)
475{
476 auto it { x_meta_map.find(k) };
477 bool r { it != x_meta_map.end() };
478 switch(a) {
479 default:
480 ceph_assert(a == 0);
481 case DISCARD:
482 break;
483 case APPEND:
484 if (r) {
485 std::string old { it->second };
486 boost::algorithm::trim_right(old);
487 old.append(",");
488 old.append(v);
489 x_meta_map[k] = old;
490 break;
491 }
492 /* fall through */
493 case OVERWRITE:
494 x_meta_map[k] = v;
495 }
496 return r;
497}
498
7c673cae
FG
499string rgw_string_unquote(const string& s)
500{
501 if (s[0] != '"' || s.size() < 2)
502 return s;
503
504 int len;
505 for (len = s.size(); len > 2; --len) {
506 if (s[len - 1] != ' ')
507 break;
508 }
509
510 if (s[len-1] != '"')
511 return s;
512
513 return s.substr(1, len - 2);
514}
515
7c673cae
FG
516static bool check_str_end(const char *s)
517{
518 if (!s)
519 return false;
520
521 while (*s) {
522 if (!isspace(*s))
523 return false;
524 s++;
525 }
526 return true;
527}
528
529static bool check_gmt_end(const char *s)
530{
531 if (!s || !*s)
532 return false;
533
534 while (isspace(*s)) {
535 ++s;
536 }
537
538 /* check for correct timezone */
539 if ((strncmp(s, "GMT", 3) != 0) &&
540 (strncmp(s, "UTC", 3) != 0)) {
541 return false;
542 }
543
544 return true;
545}
546
547static bool parse_rfc850(const char *s, struct tm *t)
548{
92f5a8d4 549 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
550 memset(t, 0, sizeof(*t));
551 return check_gmt_end(strptime(s, "%A, %d-%b-%y %H:%M:%S ", t));
552}
553
554static bool parse_asctime(const char *s, struct tm *t)
555{
92f5a8d4 556 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
557 memset(t, 0, sizeof(*t));
558 return check_str_end(strptime(s, "%a %b %d %H:%M:%S %Y", t));
559}
560
561static bool parse_rfc1123(const char *s, struct tm *t)
562{
92f5a8d4 563 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
564 memset(t, 0, sizeof(*t));
565 return check_gmt_end(strptime(s, "%a, %d %b %Y %H:%M:%S ", t));
566}
567
568static bool parse_rfc1123_alt(const char *s, struct tm *t)
569{
92f5a8d4 570 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
571 memset(t, 0, sizeof(*t));
572 return check_str_end(strptime(s, "%a, %d %b %Y %H:%M:%S %z", t));
573}
574
575bool parse_rfc2616(const char *s, struct tm *t)
576{
577 return parse_rfc850(s, t) || parse_asctime(s, t) || parse_rfc1123(s, t) || parse_rfc1123_alt(s,t);
578}
579
580bool parse_iso8601(const char *s, struct tm *t, uint32_t *pns, bool extended_format)
581{
92f5a8d4 582 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
583 memset(t, 0, sizeof(*t));
584 const char *p;
585
586 if (!s)
587 s = "";
588
589 if (extended_format) {
590 p = strptime(s, "%Y-%m-%dT%T", t);
591 if (!p) {
592 p = strptime(s, "%Y-%m-%d %T", t);
593 }
594 } else {
595 p = strptime(s, "%Y%m%dT%H%M%S", t);
596 }
597 if (!p) {
598 dout(0) << "parse_iso8601 failed" << dendl;
599 return false;
600 }
f67539c2 601 const std::string_view str = rgw_trim_whitespace(std::string_view(p));
7c673cae
FG
602 int len = str.size();
603
31f18b77 604 if (len == 0 || (len == 1 && str[0] == 'Z'))
7c673cae
FG
605 return true;
606
607 if (str[0] != '.' ||
608 str[len - 1] != 'Z')
609 return false;
610
611 uint32_t ms;
f67539c2
TL
612 std::string_view nsstr = str.substr(1, len - 2);
613 int r = stringtoul(std::string(nsstr), &ms);
7c673cae
FG
614 if (r < 0)
615 return false;
616
617 if (!pns) {
618 return true;
619 }
620
621 if (nsstr.size() > 9) {
622 nsstr = nsstr.substr(0, 9);
623 }
624
625 uint64_t mul_table[] = { 0,
626 100000000LL,
627 10000000LL,
628 1000000LL,
629 100000LL,
630 10000LL,
631 1000LL,
632 100LL,
633 10LL,
634 1 };
635
636
637 *pns = ms * mul_table[nsstr.size()];
638
639 return true;
640}
641
642int parse_key_value(string& in_str, const char *delim, string& key, string& val)
643{
644 if (delim == NULL)
645 return -EINVAL;
646
31f18b77
FG
647 auto pos = in_str.find(delim);
648 if (pos == string::npos)
7c673cae
FG
649 return -EINVAL;
650
31f18b77
FG
651 key = rgw_trim_whitespace(in_str.substr(0, pos));
652 val = rgw_trim_whitespace(in_str.substr(pos + 1));
7c673cae
FG
653
654 return 0;
655}
656
657int parse_key_value(string& in_str, string& key, string& val)
658{
659 return parse_key_value(in_str, "=", key,val);
660}
661
f67539c2
TL
662boost::optional<std::pair<std::string_view, std::string_view>>
663parse_key_value(const std::string_view& in_str,
664 const std::string_view& delim)
31f18b77
FG
665{
666 const size_t pos = in_str.find(delim);
f67539c2 667 if (pos == std::string_view::npos) {
31f18b77
FG
668 return boost::none;
669 }
670
671 const auto key = rgw_trim_whitespace(in_str.substr(0, pos));
672 const auto val = rgw_trim_whitespace(in_str.substr(pos + 1));
673
674 return std::make_pair(key, val);
675}
676
f67539c2
TL
677boost::optional<std::pair<std::string_view, std::string_view>>
678parse_key_value(const std::string_view& in_str)
31f18b77
FG
679{
680 return parse_key_value(in_str, "=");
681}
682
7c673cae
FG
683int parse_time(const char *time_str, real_time *time)
684{
685 struct tm tm;
686 uint32_t ns = 0;
687
688 if (!parse_rfc2616(time_str, &tm) && !parse_iso8601(time_str, &tm, &ns)) {
689 return -EINVAL;
690 }
691
692 time_t sec = internal_timegm(&tm);
693 *time = utime_t(sec, ns).to_real_time();
694
695 return 0;
696}
697
698#define TIME_BUF_SIZE 128
699
700void rgw_to_iso8601(const real_time& t, char *dest, int buf_size)
701{
702 utime_t ut(t);
703
704 char buf[TIME_BUF_SIZE];
705 struct tm result;
706 time_t epoch = ut.sec();
707 struct tm *tmp = gmtime_r(&epoch, &result);
708 if (tmp == NULL)
709 return;
710
711 if (strftime(buf, sizeof(buf), "%Y-%m-%dT%T", tmp) == 0)
712 return;
713
714 snprintf(dest, buf_size, "%s.%03dZ", buf, (int)(ut.usec() / 1000));
715}
716
717void rgw_to_iso8601(const real_time& t, string *dest)
718{
719 char buf[TIME_BUF_SIZE];
720 rgw_to_iso8601(t, buf, sizeof(buf));
721 *dest = buf;
722}
723
c07f9fc5
FG
724
725string rgw_to_asctime(const utime_t& t)
726{
727 stringstream s;
728 t.asctime(s);
729 return s.str();
730}
731
7c673cae
FG
732/*
733 * calculate the sha1 value of a given msg and key
734 */
735void calc_hmac_sha1(const char *key, int key_len,
736 const char *msg, int msg_len, char *dest)
737/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
738{
739 HMACSHA1 hmac((const unsigned char *)key, key_len);
740 hmac.Update((const unsigned char *)msg, msg_len);
741 hmac.Final((unsigned char *)dest);
742}
743
744/*
745 * calculate the sha256 value of a given msg and key
746 */
747void calc_hmac_sha256(const char *key, int key_len,
748 const char *msg, int msg_len, char *dest)
749{
750 char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
751
752 HMACSHA256 hmac((const unsigned char *)key, key_len);
753 hmac.Update((const unsigned char *)msg, msg_len);
754 hmac.Final((unsigned char *)hash_sha256);
755
756 memcpy(dest, hash_sha256, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE);
757}
758
31f18b77
FG
759using ceph::crypto::SHA256;
760
7c673cae
FG
761/*
762 * calculate the sha256 hash value of a given msg
763 */
f67539c2 764sha256_digest_t calc_hash_sha256(const std::string_view& msg)
7c673cae 765{
11fdf7f2 766 sha256_digest_t hash;
7c673cae 767
31f18b77
FG
768 SHA256 hasher;
769 hasher.Update(reinterpret_cast<const unsigned char*>(msg.data()), msg.size());
11fdf7f2 770 hasher.Final(hash.v);
7c673cae 771
31f18b77 772 return hash;
7c673cae
FG
773}
774
7c673cae
FG
775SHA256* calc_hash_sha256_open_stream()
776{
777 return new SHA256;
778}
779
780void calc_hash_sha256_update_stream(SHA256 *hash, const char *msg, int len)
781{
782 hash->Update((const unsigned char *)msg, len);
783}
784
785string calc_hash_sha256_close_stream(SHA256 **phash)
786{
787 SHA256 *hash = *phash;
788 if (!hash) {
789 hash = calc_hash_sha256_open_stream();
790 }
791 char hash_sha256[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE];
792
793 hash->Final((unsigned char *)hash_sha256);
794
795 char hex_str[(CEPH_CRYPTO_SHA256_DIGESTSIZE * 2) + 1];
796 buf_to_hex((unsigned char *)hash_sha256, CEPH_CRYPTO_SHA256_DIGESTSIZE, hex_str);
797
798 delete hash;
799 *phash = NULL;
800
801 return std::string(hex_str);
802}
803
31f18b77
FG
804std::string calc_hash_sha256_restart_stream(SHA256 **phash)
805{
806 const auto hash = calc_hash_sha256_close_stream(phash);
807 *phash = calc_hash_sha256_open_stream();
808
809 return hash;
810}
811
7c673cae
FG
812int NameVal::parse()
813{
31f18b77 814 auto delim_pos = str.find('=');
7c673cae
FG
815 int ret = 0;
816
31f18b77 817 if (delim_pos == string::npos) {
7c673cae
FG
818 name = str;
819 val = "";
820 ret = 1;
821 } else {
822 name = str.substr(0, delim_pos);
823 val = str.substr(delim_pos + 1);
824 }
825
826 return ret;
827}
828
b3b6e05e 829int RGWHTTPArgs::parse(const DoutPrefixProvider *dpp)
7c673cae
FG
830{
831 int pos = 0;
832 bool end = false;
833
834 if (str.empty())
835 return 0;
836
837 if (str[pos] == '?')
838 pos++;
839
840 while (!end) {
841 int fpos = str.find('&', pos);
842 if (fpos < pos) {
843 end = true;
844 fpos = str.size();
845 }
31f18b77
FG
846 std::string nameval = url_decode(str.substr(pos, fpos - pos), true);
847 NameVal nv(std::move(nameval));
7c673cae
FG
848 int ret = nv.parse();
849 if (ret >= 0) {
850 string& name = nv.get_name();
f67539c2
TL
851 if (name.find("X-Amz-") != string::npos) {
852 std::for_each(name.begin(),
853 name.end(),
854 [](char &c){
855 if (c != '-') {
856 c = ::tolower(static_cast<unsigned char>(c));
857 }
858 });
859 }
7c673cae 860 string& val = nv.get_val();
b3b6e05e 861 ldpp_dout(dpp, 10) << "name: " << name << " val: " << val << dendl;
7c673cae
FG
862 append(name, val);
863 }
864
865 pos = fpos + 1;
866 }
867
868 return 0;
869}
870
871void RGWHTTPArgs::append(const string& name, const string& val)
872{
873 if (name.compare(0, sizeof(RGW_SYS_PARAM_PREFIX) - 1, RGW_SYS_PARAM_PREFIX) == 0) {
874 sys_val_map[name] = val;
875 } else {
876 val_map[name] = val;
877 }
878
20effc67 879// when sub_resources exclusive by object are added, please remember to update obj_sub_resource in RGWHTTPArgs::exist_obj_excl_sub_resource().
7c673cae
FG
880 if ((name.compare("acl") == 0) ||
881 (name.compare("cors") == 0) ||
11fdf7f2 882 (name.compare("notification") == 0) ||
7c673cae
FG
883 (name.compare("location") == 0) ||
884 (name.compare("logging") == 0) ||
885 (name.compare("usage") == 0) ||
886 (name.compare("lifecycle") == 0) ||
887 (name.compare("delete") == 0) ||
888 (name.compare("uploads") == 0) ||
889 (name.compare("partNumber") == 0) ||
890 (name.compare("uploadId") == 0) ||
891 (name.compare("versionId") == 0) ||
892 (name.compare("start-date") == 0) ||
893 (name.compare("end-date") == 0) ||
894 (name.compare("versions") == 0) ||
895 (name.compare("versioning") == 0) ||
896 (name.compare("website") == 0) ||
897 (name.compare("requestPayment") == 0) ||
224ce89b 898 (name.compare("torrent") == 0) ||
11fdf7f2
TL
899 (name.compare("tagging") == 0) ||
900 (name.compare("append") == 0) ||
9f95a23c
TL
901 (name.compare("position") == 0) ||
902 (name.compare("policyStatus") == 0) ||
903 (name.compare("publicAccessBlock") == 0)) {
7c673cae
FG
904 sub_resources[name] = val;
905 } else if (name[0] == 'r') { // root of all evil
906 if ((name.compare("response-content-type") == 0) ||
907 (name.compare("response-content-language") == 0) ||
908 (name.compare("response-expires") == 0) ||
909 (name.compare("response-cache-control") == 0) ||
910 (name.compare("response-content-disposition") == 0) ||
911 (name.compare("response-content-encoding") == 0)) {
912 sub_resources[name] = val;
913 has_resp_modifier = true;
914 }
915 } else if ((name.compare("subuser") == 0) ||
916 (name.compare("key") == 0) ||
917 (name.compare("caps") == 0) ||
918 (name.compare("index") == 0) ||
919 (name.compare("policy") == 0) ||
920 (name.compare("quota") == 0) ||
11fdf7f2 921 (name.compare("list") == 0) ||
9f95a23c
TL
922 (name.compare("object") == 0) ||
923 (name.compare("sync") == 0)) {
7c673cae
FG
924 if (!admin_subresource_added) {
925 sub_resources[name] = "";
926 admin_subresource_added = true;
927 }
928 }
929}
930
931const string& RGWHTTPArgs::get(const string& name, bool *exists) const
932{
933 auto iter = val_map.find(name);
934 bool e = (iter != std::end(val_map));
935 if (exists)
936 *exists = e;
937 if (e)
938 return iter->second;
939 return empty_str;
940}
941
d2e6a577
FG
942boost::optional<const std::string&>
943RGWHTTPArgs::get_optional(const std::string& name) const
944{
945 bool exists;
946 const std::string& value = get(name, &exists);
947 if (exists) {
948 return value;
949 } else {
950 return boost::none;
951 }
952}
953
20effc67 954int RGWHTTPArgs::get_bool(const string& name, bool *val, bool *exists) const
7c673cae 955{
20effc67 956 map<string, string>::const_iterator iter;
7c673cae
FG
957 iter = val_map.find(name);
958 bool e = (iter != val_map.end());
959 if (exists)
960 *exists = e;
961
962 if (e) {
963 const char *s = iter->second.c_str();
964
965 if (strcasecmp(s, "false") == 0) {
966 *val = false;
967 } else if (strcasecmp(s, "true") == 0) {
968 *val = true;
969 } else {
970 return -EINVAL;
971 }
972 }
973
974 return 0;
975}
976
20effc67 977int RGWHTTPArgs::get_bool(const char *name, bool *val, bool *exists) const
7c673cae
FG
978{
979 string s(name);
980 return get_bool(s, val, exists);
981}
982
20effc67 983void RGWHTTPArgs::get_bool(const char *name, bool *val, bool def_val) const
7c673cae
FG
984{
985 bool exists = false;
986 if ((get_bool(name, val, &exists) < 0) ||
987 !exists) {
988 *val = def_val;
989 }
990}
991
20effc67 992int RGWHTTPArgs::get_int(const char *name, int *val, int def_val) const
11fdf7f2
TL
993{
994 bool exists = false;
995 string val_str;
996 val_str = get(name, &exists);
997 if (!exists) {
998 *val = def_val;
999 return 0;
1000 }
1001
1002 string err;
1003
1004 *val = (int)strict_strtol(val_str.c_str(), 10, &err);
1005 if (!err.empty()) {
1006 *val = def_val;
1007 return -EINVAL;
1008 }
1009 return 0;
1010}
1011
7c673cae
FG
1012string RGWHTTPArgs::sys_get(const string& name, bool * const exists) const
1013{
1014 const auto iter = sys_val_map.find(name);
1015 const bool e = (iter != sys_val_map.end());
1016
1017 if (exists) {
1018 *exists = e;
1019 }
1020
1021 return e ? iter->second : string();
1022}
1023
f64942e4
AA
1024bool rgw_transport_is_secure(CephContext *cct, const RGWEnv& env)
1025{
1026 const auto& m = env.get_map();
1027 // frontend connected with ssl
1028 if (m.count("SERVER_PORT_SECURE")) {
1029 return true;
1030 }
1031 // ignore proxy headers unless explicitly enabled
1032 if (!cct->_conf->rgw_trust_forwarded_https) {
1033 return false;
1034 }
1035 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
1036 // Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>
1037 auto i = m.find("HTTP_FORWARDED");
1038 if (i != m.end() && i->second.find("proto=https") != std::string::npos) {
1039 return true;
1040 }
1041 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
1042 i = m.find("HTTP_X_FORWARDED_PROTO");
1043 if (i != m.end() && i->second == "https") {
1044 return true;
1045 }
1046 return false;
1047}
1048
9f95a23c 1049
11fdf7f2 1050namespace {
9f95a23c
TL
1051
1052struct perm_state_from_req_state : public perm_state_base {
1053 req_state * const s;
f67539c2
TL
1054 perm_state_from_req_state(req_state * const _s)
1055 : perm_state_base(_s->cct,
1056 _s->env,
1057 _s->auth.identity.get(),
1058 _s->bucket.get() ? _s->bucket->get_info() : RGWBucketInfo(),
1059 _s->perm_mask,
1060 _s->defer_to_bucket_acls,
1061 _s->bucket_access_conf),
1062 s(_s) {}
1063
9f95a23c
TL
1064 std::optional<bool> get_request_payer() const override {
1065 const char *request_payer = s->info.env->get("HTTP_X_AMZ_REQUEST_PAYER");
1066 if (!request_payer) {
1067 bool exists;
1068 request_payer = s->info.args.get("x-amz-request-payer", &exists).c_str();
1069 if (!exists) {
1070 return false;
1071 }
1072 }
1073
1074 if (strcasecmp(request_payer, "requester") == 0) {
1075 return true;
1076 }
1077
1078 return std::nullopt;
1079 }
1080
1081 const char *get_referer() const override {
1082 return s->info.env->get("HTTP_REFERER");
1083 }
1084};
1085
11fdf7f2
TL
1086Effect eval_or_pass(const boost::optional<Policy>& policy,
1087 const rgw::IAM::Environment& env,
1088 boost::optional<const rgw::auth::Identity&> id,
1089 const uint64_t op,
20effc67 1090 const ARN& resource,
522d829b 1091 boost::optional<rgw::IAM::PolicyPrincipal&> princ_type=boost::none) {
11fdf7f2
TL
1092 if (!policy)
1093 return Effect::Pass;
1094 else
20effc67 1095 return policy->eval(env, id, op, resource, princ_type);
11fdf7f2
TL
1096}
1097
1098}
1099
522d829b 1100Effect eval_identity_or_session_policies(const vector<Policy>& policies,
11fdf7f2 1101 const rgw::IAM::Environment& env,
11fdf7f2
TL
1102 const uint64_t op,
1103 const ARN& arn) {
522d829b
TL
1104 auto policy_res = Effect::Pass, prev_res = Effect::Pass;
1105 for (auto& policy : policies) {
20effc67 1106 if (policy_res = eval_or_pass(policy, env, boost::none, op, arn); policy_res == Effect::Deny)
522d829b
TL
1107 return policy_res;
1108 else if (policy_res == Effect::Allow)
11fdf7f2 1109 prev_res = Effect::Allow;
522d829b
TL
1110 else if (policy_res == Effect::Pass && prev_res == Effect::Allow)
1111 policy_res = Effect::Allow;
11fdf7f2 1112 }
522d829b 1113 return policy_res;
11fdf7f2
TL
1114}
1115
1116bool verify_user_permission(const DoutPrefixProvider* dpp,
9f95a23c 1117 perm_state_base * const s,
11fdf7f2
TL
1118 RGWAccessControlPolicy * const user_acl,
1119 const vector<rgw::IAM::Policy>& user_policies,
20effc67 1120 const vector<rgw::IAM::Policy>& session_policies,
eafe8130 1121 const rgw::ARN& res,
11fdf7f2
TL
1122 const uint64_t op)
1123{
20effc67
TL
1124 auto identity_policy_res = eval_identity_or_session_policies(user_policies, s->env, op, res);
1125 if (identity_policy_res == Effect::Deny) {
1126 return false;
1127 }
1128
1129 if (! session_policies.empty()) {
1130 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, op, res);
1131 if (session_policy_res == Effect::Deny) {
1132 return false;
1133 }
1134 //Intersection of identity policies and session policies
1135 if (identity_policy_res == Effect::Allow && session_policy_res == Effect::Allow) {
1136 return true;
1137 }
11fdf7f2
TL
1138 return false;
1139 }
1140
20effc67 1141 if (identity_policy_res == Effect::Allow) {
11fdf7f2
TL
1142 return true;
1143 }
1144
1145 if (op == rgw::IAM::s3CreateBucket || op == rgw::IAM::s3ListAllMyBuckets) {
1146 auto perm = op_to_perm(op);
1147
1148 return verify_user_permission_no_policy(dpp, s, user_acl, perm);
1149 }
1150
1151 return false;
1152}
1153
9f95a23c
TL
1154bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1155 struct perm_state_base * const s,
1156 RGWAccessControlPolicy * const user_acl,
1157 const int perm)
7c673cae 1158{
9f95a23c 1159 if (s->identity->get_identity_type() == TYPE_ROLE)
11fdf7f2
TL
1160 return false;
1161
7c673cae
FG
1162 /* S3 doesn't support account ACLs. */
1163 if (!user_acl)
1164 return true;
1165
1166 if ((perm & (int)s->perm_mask) != perm)
1167 return false;
1168
9f95a23c 1169 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
7c673cae
FG
1170}
1171
11fdf7f2
TL
1172bool verify_user_permission(const DoutPrefixProvider* dpp,
1173 struct req_state * const s,
eafe8130 1174 const rgw::ARN& res,
11fdf7f2
TL
1175 const uint64_t op)
1176{
9f95a23c 1177 perm_state_from_req_state ps(s);
20effc67 1178 return verify_user_permission(dpp, &ps, s->user_acl.get(), s->iam_user_policies, s->session_policies, res, op);
11fdf7f2
TL
1179}
1180
1181bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1182 struct req_state * const s,
1183 const int perm)
7c673cae 1184{
9f95a23c
TL
1185 perm_state_from_req_state ps(s);
1186 return verify_user_permission_no_policy(dpp, &ps, s->user_acl.get(), perm);
7c673cae
FG
1187}
1188
9f95a23c 1189bool verify_requester_payer_permission(struct perm_state_base *s)
7c673cae
FG
1190{
1191 if (!s->bucket_info.requester_pays)
1192 return true;
1193
9f95a23c 1194 if (s->identity->is_owner_of(s->bucket_info.owner))
7c673cae
FG
1195 return true;
1196
9f95a23c 1197 if (s->identity->is_anonymous()) {
7c673cae
FG
1198 return false;
1199 }
1200
9f95a23c
TL
1201 auto request_payer = s->get_request_payer();
1202 if (request_payer) {
1203 return *request_payer;
7c673cae
FG
1204 }
1205
1206 return false;
1207}
1208
11fdf7f2 1209bool verify_bucket_permission(const DoutPrefixProvider* dpp,
9f95a23c 1210 struct perm_state_base * const s,
31f18b77 1211 const rgw_bucket& bucket,
7c673cae
FG
1212 RGWAccessControlPolicy * const user_acl,
1213 RGWAccessControlPolicy * const bucket_acl,
11fdf7f2 1214 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1215 const vector<Policy>& identity_policies,
1216 const vector<Policy>& session_policies,
31f18b77 1217 const uint64_t op)
7c673cae 1218{
31f18b77 1219 if (!verify_requester_payer_permission(s))
7c673cae
FG
1220 return false;
1221
20effc67 1222 auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, op, ARN(bucket));
522d829b 1223 if (identity_policy_res == Effect::Deny)
11fdf7f2
TL
1224 return false;
1225
522d829b 1226 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
9f95a23c 1227 auto r = eval_or_pass(bucket_policy, s->env, *s->identity,
522d829b
TL
1228 op, ARN(bucket), princ_type);
1229 if (r == Effect::Deny)
1230 return false;
1231
1232 //Take into account session policies, if the identity making a request is a role
1233 if (!session_policies.empty()) {
20effc67 1234 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, op, ARN(bucket));
522d829b
TL
1235 if (session_policy_res == Effect::Deny) {
1236 return false;
1237 }
1238 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1239 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1240 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1241 (session_policy_res == Effect::Allow && r == Effect::Allow))
1242 return true;
1243 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1244 //Intersection of session policy and identity policy plus bucket policy
1245 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow)
1246 return true;
1247 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1248 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1249 return true;
1250 }
1251 return false;
1252 }
1253
1254 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
b32b8144
FG
1255 // It looks like S3 ACLs only GRANT permissions rather than
1256 // denying them, so this should be safe.
1257 return true;
31f18b77
FG
1258
1259 const auto perm = op_to_perm(op);
1260
11fdf7f2 1261 return verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm);
31f18b77
FG
1262}
1263
9f95a23c
TL
1264bool verify_bucket_permission(const DoutPrefixProvider* dpp,
1265 struct req_state * const s,
1266 const rgw_bucket& bucket,
1267 RGWAccessControlPolicy * const user_acl,
1268 RGWAccessControlPolicy * const bucket_acl,
1269 const boost::optional<Policy>& bucket_policy,
1270 const vector<Policy>& user_policies,
522d829b 1271 const vector<Policy>& session_policies,
9f95a23c
TL
1272 const uint64_t op)
1273{
1274 perm_state_from_req_state ps(s);
1275 return verify_bucket_permission(dpp, &ps, bucket,
1276 user_acl, bucket_acl,
1277 bucket_policy, user_policies,
522d829b 1278 session_policies, op);
9f95a23c
TL
1279}
1280
1281bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
31f18b77
FG
1282 RGWAccessControlPolicy * const user_acl,
1283 RGWAccessControlPolicy * const bucket_acl,
1284 const int perm)
1285{
1286 if (!bucket_acl)
7c673cae
FG
1287 return false;
1288
31f18b77 1289 if ((perm & (int)s->perm_mask) != perm)
7c673cae
FG
1290 return false;
1291
9f95a23c
TL
1292 if (bucket_acl->verify_permission(dpp, *s->identity, perm, perm,
1293 s->get_referer(),
1294 s->bucket_access_conf &&
1295 s->bucket_access_conf->ignore_public_acls()))
7c673cae
FG
1296 return true;
1297
1298 if (!user_acl)
1299 return false;
1300
9f95a23c
TL
1301 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
1302}
1303
1304bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s,
1305 RGWAccessControlPolicy * const user_acl,
1306 RGWAccessControlPolicy * const bucket_acl,
1307 const int perm)
1308{
1309 perm_state_from_req_state ps(s);
1310 return verify_bucket_permission_no_policy(dpp,
1311 &ps,
1312 user_acl,
1313 bucket_acl,
1314 perm);
7c673cae
FG
1315}
1316
11fdf7f2 1317bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s, const int perm)
31f18b77 1318{
9f95a23c
TL
1319 perm_state_from_req_state ps(s);
1320
1321 if (!verify_requester_payer_permission(&ps))
31f18b77
FG
1322 return false;
1323
11fdf7f2 1324 return verify_bucket_permission_no_policy(dpp,
9f95a23c 1325 &ps,
c07f9fc5
FG
1326 s->user_acl.get(),
1327 s->bucket_acl.get(),
1328 perm);
31f18b77
FG
1329}
1330
11fdf7f2 1331bool verify_bucket_permission(const DoutPrefixProvider* dpp, struct req_state * const s, const uint64_t op)
7c673cae 1332{
2a845540
TL
1333 if (rgw::sal::Bucket::empty(s->bucket)) {
1334 // request is missing a bucket name
1335 return false;
1336 }
1337
9f95a23c
TL
1338 perm_state_from_req_state ps(s);
1339
11fdf7f2 1340 return verify_bucket_permission(dpp,
9f95a23c 1341 &ps,
f67539c2 1342 s->bucket->get_key(),
7c673cae 1343 s->user_acl.get(),
c07f9fc5
FG
1344 s->bucket_acl.get(),
1345 s->iam_policy,
11fdf7f2 1346 s->iam_user_policies,
522d829b 1347 s->session_policies,
31f18b77
FG
1348 op);
1349}
1350
522d829b 1351// Authorize anyone permitted by the bucket policy, identity policies, session policies and the bucket owner
b32b8144
FG
1352// unless explicitly denied by the policy.
1353
1354int verify_bucket_owner_or_policy(struct req_state* const s,
1355 const uint64_t op)
1356{
20effc67 1357 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, op, ARN(s->bucket->get_key()));
522d829b 1358 if (identity_policy_res == Effect::Deny) {
f91f0fd5
TL
1359 return -EACCES;
1360 }
1361
522d829b 1362 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
b32b8144
FG
1363 auto e = eval_or_pass(s->iam_policy,
1364 s->env, *s->auth.identity,
522d829b 1365 op, ARN(s->bucket->get_key()), princ_type);
f91f0fd5
TL
1366 if (e == Effect::Deny) {
1367 return -EACCES;
1368 }
1369
522d829b 1370 if (!s->session_policies.empty()) {
20effc67 1371 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, op, ARN(s->bucket->get_key()));
522d829b
TL
1372 if (session_policy_res == Effect::Deny) {
1373 return -EACCES;
1374 }
1375 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1376 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1377 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1378 (session_policy_res == Effect::Allow && e == Effect::Allow))
1379 return 0;
1380 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1381 //Intersection of session policy and identity policy plus bucket policy
1382 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow)
1383 return 0;
1384 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1385 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1386 return 0;
1387 }
1388 return -EACCES;
1389 }
1390
b32b8144 1391 if (e == Effect::Allow ||
522d829b 1392 identity_policy_res == Effect::Allow ||
b32b8144 1393 (e == Effect::Pass &&
522d829b 1394 identity_policy_res == Effect::Pass &&
b32b8144
FG
1395 s->auth.identity->is_owner_of(s->bucket_owner.get_id()))) {
1396 return 0;
1397 } else {
1398 return -EACCES;
1399 }
1400}
1401
1402
11fdf7f2 1403static inline bool check_deferred_bucket_perms(const DoutPrefixProvider* dpp,
9f95a23c 1404 struct perm_state_base * const s,
31f18b77
FG
1405 const rgw_bucket& bucket,
1406 RGWAccessControlPolicy * const user_acl,
1407 RGWAccessControlPolicy * const bucket_acl,
11fdf7f2 1408 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1409 const vector<Policy>& identity_policies,
1410 const vector<Policy>& session_policies,
31f18b77
FG
1411 const uint8_t deferred_check,
1412 const uint64_t op)
1413{
1414 return (s->defer_to_bucket_acls == deferred_check \
522d829b 1415 && verify_bucket_permission(dpp, s, bucket, user_acl, bucket_acl, bucket_policy, identity_policies, session_policies,op));
7c673cae
FG
1416}
1417
11fdf7f2 1418static inline bool check_deferred_bucket_only_acl(const DoutPrefixProvider* dpp,
9f95a23c 1419 struct perm_state_base * const s,
31f18b77
FG
1420 RGWAccessControlPolicy * const user_acl,
1421 RGWAccessControlPolicy * const bucket_acl,
1422 const uint8_t deferred_check,
1423 const int perm)
7c673cae
FG
1424{
1425 return (s->defer_to_bucket_acls == deferred_check \
11fdf7f2 1426 && verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm));
7c673cae
FG
1427}
1428
9f95a23c 1429bool verify_object_permission(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
31f18b77 1430 const rgw_obj& obj,
7c673cae
FG
1431 RGWAccessControlPolicy * const user_acl,
1432 RGWAccessControlPolicy * const bucket_acl,
1433 RGWAccessControlPolicy * const object_acl,
11fdf7f2 1434 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1435 const vector<Policy>& identity_policies,
1436 const vector<Policy>& session_policies,
31f18b77 1437 const uint64_t op)
7c673cae
FG
1438{
1439 if (!verify_requester_payer_permission(s))
1440 return false;
1441
20effc67 1442 auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, op, ARN(obj));
522d829b 1443 if (identity_policy_res == Effect::Deny)
11fdf7f2 1444 return false;
b32b8144 1445
522d829b
TL
1446 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
1447 auto r = eval_or_pass(bucket_policy, s->env, *s->identity, op, ARN(obj), princ_type);
1448 if (r == Effect::Deny)
1449 return false;
1450
1451 if (!session_policies.empty()) {
20effc67 1452 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, op, ARN(obj));
522d829b
TL
1453 if (session_policy_res == Effect::Deny) {
1454 return false;
1455 }
1456 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1457 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1458 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1459 (session_policy_res == Effect::Allow && r == Effect::Allow))
1460 return true;
1461 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1462 //Intersection of session policy and identity policy plus bucket policy
1463 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow)
1464 return true;
1465 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1466 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1467 return true;
1468 }
1469 return false;
1470 }
1471
1472 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
b32b8144
FG
1473 // It looks like S3 ACLs only GRANT permissions rather than
1474 // denying them, so this should be safe.
1475 return true;
31f18b77
FG
1476
1477 const auto perm = op_to_perm(op);
1478
11fdf7f2 1479 if (check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
522d829b 1480 identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
11fdf7f2 1481 check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
522d829b 1482 identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
31f18b77
FG
1483 return true;
1484 }
1485
1486 if (!object_acl) {
1487 return false;
1488 }
1489
9f95a23c
TL
1490 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1491 nullptr, /* http_referrer */
1492 s->bucket_access_conf &&
1493 s->bucket_access_conf->ignore_public_acls());
31f18b77
FG
1494 if (ret) {
1495 return true;
1496 }
1497
1498 if (!s->cct->_conf->rgw_enforce_swift_acls)
1499 return ret;
1500
1501 if ((perm & (int)s->perm_mask) != perm)
1502 return false;
1503
1504 int swift_perm = 0;
1505 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1506 swift_perm |= RGW_PERM_READ_OBJS;
1507 if (perm & RGW_PERM_WRITE)
1508 swift_perm |= RGW_PERM_WRITE_OBJS;
1509
1510 if (!swift_perm)
1511 return false;
1512
1513 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1514 otherwise the mask might not cover the swift permissions bits */
9f95a23c
TL
1515 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1516 s->get_referer()))
31f18b77
FG
1517 return true;
1518
1519 if (!user_acl)
1520 return false;
1521
9f95a23c
TL
1522 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
1523}
1524
1525bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state * const s,
1526 const rgw_obj& obj,
1527 RGWAccessControlPolicy * const user_acl,
1528 RGWAccessControlPolicy * const bucket_acl,
1529 RGWAccessControlPolicy * const object_acl,
1530 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1531 const vector<Policy>& identity_policies,
1532 const vector<Policy>& session_policies,
9f95a23c
TL
1533 const uint64_t op)
1534{
1535 perm_state_from_req_state ps(s);
1536 return verify_object_permission(dpp, &ps, obj,
1537 user_acl, bucket_acl,
1538 object_acl, bucket_policy,
522d829b 1539 identity_policies, session_policies, op);
31f18b77
FG
1540}
1541
11fdf7f2 1542bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp,
9f95a23c 1543 struct perm_state_base * const s,
31f18b77
FG
1544 RGWAccessControlPolicy * const user_acl,
1545 RGWAccessControlPolicy * const bucket_acl,
1546 RGWAccessControlPolicy * const object_acl,
1547 const int perm)
1548{
11fdf7f2
TL
1549 if (check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, perm) ||
1550 check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, RGW_PERM_FULL_CONTROL)) {
7c673cae
FG
1551 return true;
1552 }
1553
1554 if (!object_acl) {
1555 return false;
1556 }
1557
9f95a23c
TL
1558 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1559 nullptr, /* http referrer */
1560 s->bucket_access_conf &&
1561 s->bucket_access_conf->ignore_public_acls());
7c673cae
FG
1562 if (ret) {
1563 return true;
1564 }
1565
1566 if (!s->cct->_conf->rgw_enforce_swift_acls)
1567 return ret;
1568
1569 if ((perm & (int)s->perm_mask) != perm)
1570 return false;
1571
1572 int swift_perm = 0;
1573 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1574 swift_perm |= RGW_PERM_READ_OBJS;
1575 if (perm & RGW_PERM_WRITE)
1576 swift_perm |= RGW_PERM_WRITE_OBJS;
1577
1578 if (!swift_perm)
1579 return false;
1580
1581 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1582 otherwise the mask might not cover the swift permissions bits */
9f95a23c
TL
1583 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1584 s->get_referer()))
7c673cae
FG
1585 return true;
1586
1587 if (!user_acl)
1588 return false;
1589
9f95a23c 1590 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
7c673cae
FG
1591}
1592
11fdf7f2 1593bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state *s, int perm)
31f18b77 1594{
9f95a23c
TL
1595 perm_state_from_req_state ps(s);
1596
1597 if (!verify_requester_payer_permission(&ps))
31f18b77
FG
1598 return false;
1599
11fdf7f2 1600 return verify_object_permission_no_policy(dpp,
9f95a23c 1601 &ps,
c07f9fc5
FG
1602 s->user_acl.get(),
1603 s->bucket_acl.get(),
1604 s->object_acl.get(),
1605 perm);
31f18b77
FG
1606}
1607
11fdf7f2 1608bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s, uint64_t op)
7c673cae 1609{
9f95a23c
TL
1610 perm_state_from_req_state ps(s);
1611
11fdf7f2 1612 return verify_object_permission(dpp,
9f95a23c 1613 &ps,
f67539c2 1614 rgw_obj(s->bucket->get_key(), s->object->get_key()),
c07f9fc5
FG
1615 s->user_acl.get(),
1616 s->bucket_acl.get(),
1617 s->object_acl.get(),
1618 s->iam_policy,
11fdf7f2 1619 s->iam_user_policies,
522d829b 1620 s->session_policies,
31f18b77 1621 op);
7c673cae
FG
1622}
1623
f67539c2 1624
20effc67 1625int verify_object_lock(const DoutPrefixProvider* dpp, const rgw::sal::Attrs& attrs, const bool bypass_perm, const bool bypass_governance_mode) {
f67539c2
TL
1626 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
1627 if (aiter != attrs.end()) {
1628 RGWObjectRetention obj_retention;
1629 try {
1630 decode(obj_retention, aiter->second);
1631 } catch (buffer::error& err) {
1632 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
1633 return -EIO;
1634 }
1635 if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) > ceph_clock_now()) {
1636 if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
1637 return -EACCES;
1638 }
1639 }
1640 }
1641 aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD);
1642 if (aiter != attrs.end()) {
1643 RGWObjectLegalHold obj_legal_hold;
1644 try {
1645 decode(obj_legal_hold, aiter->second);
1646 } catch (buffer::error& err) {
1647 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl;
1648 return -EIO;
1649 }
1650 if (obj_legal_hold.is_enabled()) {
1651 return -EACCES;
1652 }
1653 }
1654
1655 return 0;
1656}
1657
1658
7c673cae
FG
1659class HexTable
1660{
1661 char table[256];
1662
1663public:
1664 HexTable() {
92f5a8d4 1665 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
1666 memset(table, -1, sizeof(table));
1667 int i;
1668 for (i = '0'; i<='9'; i++)
1669 table[i] = i - '0';
1670 for (i = 'A'; i<='F'; i++)
1671 table[i] = i - 'A' + 0xa;
1672 for (i = 'a'; i<='f'; i++)
1673 table[i] = i - 'a' + 0xa;
1674 }
1675
1676 char to_num(char c) {
1677 return table[(int)c];
1678 }
1679};
1680
1681static char hex_to_num(char c)
1682{
1683 static HexTable hex_table;
1684 return hex_table.to_num(c);
1685}
1686
f67539c2 1687std::string url_decode(const std::string_view& src_str, bool in_query)
7c673cae 1688{
31f18b77
FG
1689 std::string dest_str;
1690 dest_str.reserve(src_str.length() + 1);
7c673cae 1691
31f18b77 1692 for (auto src = std::begin(src_str); src != std::end(src_str); ++src) {
7c673cae
FG
1693 if (*src != '%') {
1694 if (!in_query || *src != '+') {
31f18b77
FG
1695 if (*src == '?') {
1696 in_query = true;
1697 }
1698 dest_str.push_back(*src);
7c673cae 1699 } else {
31f18b77 1700 dest_str.push_back(' ');
7c673cae
FG
1701 }
1702 } else {
31f18b77
FG
1703 /* 3 == strlen("%%XX") */
1704 if (std::distance(src, std::end(src_str)) < 3) {
7c673cae 1705 break;
31f18b77
FG
1706 }
1707
1708 src++;
1709 const char c1 = hex_to_num(*src++);
1710 const char c2 = hex_to_num(*src);
1711 if (c1 < 0 || c2 < 0) {
1712 return std::string();
1713 } else {
1714 dest_str.push_back(c1 << 4 | c2);
1715 }
7c673cae
FG
1716 }
1717 }
7c673cae 1718
31f18b77 1719 return dest_str;
7c673cae
FG
1720}
1721
1722void rgw_uri_escape_char(char c, string& dst)
1723{
1724 char buf[16];
1725 snprintf(buf, sizeof(buf), "%%%.2X", (int)(unsigned char)c);
1726 dst.append(buf);
1727}
1728
1729static bool char_needs_url_encoding(char c)
1730{
1731 if (c <= 0x20 || c >= 0x7f)
1732 return true;
1733
1734 switch (c) {
1735 case 0x22:
1736 case 0x23:
1737 case 0x25:
1738 case 0x26:
1739 case 0x2B:
1740 case 0x2C:
1741 case 0x2F:
1742 case 0x3A:
1743 case 0x3B:
1744 case 0x3C:
1745 case 0x3E:
1746 case 0x3D:
1747 case 0x3F:
1748 case 0x40:
1749 case 0x5B:
1750 case 0x5D:
1751 case 0x5C:
1752 case 0x5E:
1753 case 0x60:
1754 case 0x7B:
1755 case 0x7D:
1756 return true;
1757 }
1758 return false;
1759}
1760
11fdf7f2 1761void url_encode(const string& src, string& dst, bool encode_slash)
7c673cae
FG
1762{
1763 const char *p = src.c_str();
1764 for (unsigned i = 0; i < src.size(); i++, p++) {
11fdf7f2
TL
1765 if ((!encode_slash && *p == 0x2F) || !char_needs_url_encoding(*p)) {
1766 dst.append(p, 1);
1767 }else {
7c673cae 1768 rgw_uri_escape_char(*p, dst);
7c673cae 1769 }
7c673cae
FG
1770 }
1771}
1772
11fdf7f2 1773std::string url_encode(const std::string& src, bool encode_slash)
7c673cae
FG
1774{
1775 std::string dst;
11fdf7f2 1776 url_encode(src, dst, encode_slash);
7c673cae
FG
1777
1778 return dst;
1779}
1780
f91f0fd5
TL
1781std::string url_remove_prefix(const std::string& url)
1782{
1783 std::string dst = url;
1784 auto pos = dst.find("http://");
1785 if (pos == std::string::npos) {
1786 pos = dst.find("https://");
1787 if (pos != std::string::npos) {
1788 dst.erase(pos, 8);
1789 } else {
1790 pos = dst.find("www.");
1791 if (pos != std::string::npos) {
1792 dst.erase(pos, 4);
1793 }
1794 }
1795 } else {
1796 dst.erase(pos, 7);
1797 }
1798
1799 return dst;
1800}
1801
7c673cae
FG
1802string rgw_trim_whitespace(const string& src)
1803{
1804 if (src.empty()) {
1805 return string();
1806 }
1807
1808 int start = 0;
1809 for (; start != (int)src.size(); start++) {
1810 if (!isspace(src[start]))
1811 break;
1812 }
1813
1814 int end = src.size() - 1;
1815 if (end < start) {
1816 return string();
1817 }
1818
1819 for (; end > start; end--) {
1820 if (!isspace(src[end]))
1821 break;
1822 }
1823
1824 return src.substr(start, end - start + 1);
1825}
1826
f67539c2 1827std::string_view rgw_trim_whitespace(const std::string_view& src)
7c673cae 1828{
f67539c2 1829 std::string_view res = src;
7c673cae
FG
1830
1831 while (res.size() > 0 && std::isspace(res.front())) {
1832 res.remove_prefix(1);
1833 }
1834 while (res.size() > 0 && std::isspace(res.back())) {
1835 res.remove_suffix(1);
1836 }
1837 return res;
1838}
1839
1840string rgw_trim_quotes(const string& val)
1841{
1842 string s = rgw_trim_whitespace(val);
1843 if (s.size() < 2)
1844 return s;
1845
1846 int start = 0;
1847 int end = s.size() - 1;
1848 int quotes_count = 0;
1849
1850 if (s[start] == '"') {
1851 start++;
1852 quotes_count++;
1853 }
1854 if (s[end] == '"') {
1855 end--;
1856 quotes_count++;
1857 }
1858 if (quotes_count == 2) {
1859 return s.substr(start, end - start + 1);
1860 }
1861 return s;
1862}
1863
7c673cae
FG
1864static struct rgw_name_to_flag cap_names[] = { {"*", RGW_CAP_ALL},
1865 {"read", RGW_CAP_READ},
1866 {"write", RGW_CAP_WRITE},
1867 {NULL, 0} };
1868
1869int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
1870{
9f95a23c 1871 return rgw_parse_list_of_flags(cap_names, str, perm);
7c673cae
FG
1872}
1873
1874int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm)
1875{
1876 int pos = cap.find('=');
1877 if (pos >= 0) {
31f18b77 1878 type = rgw_trim_whitespace(cap.substr(0, pos));
7c673cae
FG
1879 }
1880
1881 if (!is_valid_cap_type(type))
1882 return -ERR_INVALID_CAP;
1883
1884 string cap_perm;
1885 uint32_t perm = 0;
1886 if (pos < (int)cap.size() - 1) {
1887 cap_perm = cap.substr(pos + 1);
1888 int r = RGWUserCaps::parse_cap_perm(cap_perm, &perm);
1889 if (r < 0)
1890 return r;
1891 }
1892
1893 *pperm = perm;
1894
1895 return 0;
1896}
1897
1898int RGWUserCaps::add_cap(const string& cap)
1899{
1900 uint32_t perm;
1901 string type;
1902
1903 int r = get_cap(cap, type, &perm);
1904 if (r < 0)
1905 return r;
1906
1907 caps[type] |= perm;
1908
1909 return 0;
1910}
1911
1912int RGWUserCaps::remove_cap(const string& cap)
1913{
1914 uint32_t perm;
1915 string type;
1916
1917 int r = get_cap(cap, type, &perm);
1918 if (r < 0)
1919 return r;
1920
1921 map<string, uint32_t>::iterator iter = caps.find(type);
1922 if (iter == caps.end())
1923 return 0;
1924
1925 uint32_t& old_perm = iter->second;
1926 old_perm &= ~perm;
1927 if (!old_perm)
1928 caps.erase(iter);
1929
1930 return 0;
1931}
1932
1933int RGWUserCaps::add_from_string(const string& str)
1934{
1935 int start = 0;
1936 do {
31f18b77
FG
1937 auto end = str.find(';', start);
1938 if (end == string::npos)
7c673cae
FG
1939 end = str.size();
1940
1941 int r = add_cap(str.substr(start, end - start));
1942 if (r < 0)
1943 return r;
1944
1945 start = end + 1;
1946 } while (start < (int)str.size());
1947
1948 return 0;
1949}
1950
1951int RGWUserCaps::remove_from_string(const string& str)
1952{
1953 int start = 0;
1954 do {
31f18b77
FG
1955 auto end = str.find(';', start);
1956 if (end == string::npos)
7c673cae
FG
1957 end = str.size();
1958
1959 int r = remove_cap(str.substr(start, end - start));
1960 if (r < 0)
1961 return r;
1962
1963 start = end + 1;
1964 } while (start < (int)str.size());
1965
1966 return 0;
1967}
1968
1969void RGWUserCaps::dump(Formatter *f) const
1970{
1971 dump(f, "caps");
1972}
1973
1974void RGWUserCaps::dump(Formatter *f, const char *name) const
1975{
1976 f->open_array_section(name);
1977 map<string, uint32_t>::const_iterator iter;
1978 for (iter = caps.begin(); iter != caps.end(); ++iter)
1979 {
1980 f->open_object_section("cap");
1981 f->dump_string("type", iter->first);
1982 uint32_t perm = iter->second;
1983 string perm_str;
1984 for (int i=0; cap_names[i].type_name; i++) {
1985 if ((perm & cap_names[i].flag) == cap_names[i].flag) {
1986 if (perm_str.size())
1987 perm_str.append(", ");
1988
1989 perm_str.append(cap_names[i].type_name);
1990 perm &= ~cap_names[i].flag;
1991 }
1992 }
1993 if (perm_str.empty())
1994 perm_str = "<none>";
1995
1996 f->dump_string("perm", perm_str);
1997 f->close_section();
1998 }
1999
2000 f->close_section();
2001}
2002
2003struct RGWUserCap {
2004 string type;
2005 uint32_t perm;
2006
2007 void decode_json(JSONObj *obj) {
2008 JSONDecoder::decode_json("type", type, obj);
2009 string perm_str;
2010 JSONDecoder::decode_json("perm", perm_str, obj);
2011 if (RGWUserCaps::parse_cap_perm(perm_str, &perm) < 0) {
2012 throw JSONDecoder::err("failed to parse permissions");
2013 }
2014 }
2015};
2016
2017void RGWUserCaps::decode_json(JSONObj *obj)
2018{
2019 list<RGWUserCap> caps_list;
2020 decode_json_obj(caps_list, obj);
2021
2022 list<RGWUserCap>::iterator iter;
2023 for (iter = caps_list.begin(); iter != caps_list.end(); ++iter) {
2024 RGWUserCap& cap = *iter;
2025 caps[cap.type] = cap.perm;
2026 }
2027}
2028
9f95a23c 2029int RGWUserCaps::check_cap(const string& cap, uint32_t perm) const
7c673cae 2030{
9f95a23c 2031 auto iter = caps.find(cap);
7c673cae
FG
2032
2033 if ((iter == caps.end()) ||
2034 (iter->second & perm) != perm) {
2035 return -EPERM;
2036 }
2037
2038 return 0;
2039}
2040
2041bool RGWUserCaps::is_valid_cap_type(const string& tp)
2042{
2043 static const char *cap_type[] = { "user",
2044 "users",
2045 "buckets",
2046 "metadata",
20effc67 2047 "info",
7c673cae
FG
2048 "usage",
2049 "zone",
2050 "bilog",
2051 "mdlog",
2052 "datalog",
11fdf7f2 2053 "roles",
f6b5b4d7 2054 "user-policy",
f91f0fd5 2055 "amz-cache",
20effc67
TL
2056 "oidc-provider",
2057 "ratelimit"};
7c673cae
FG
2058
2059 for (unsigned int i = 0; i < sizeof(cap_type) / sizeof(char *); ++i) {
2060 if (tp.compare(cap_type[i]) == 0) {
2061 return true;
2062 }
2063 }
2064
2065 return false;
2066}
2067
7c673cae
FG
2068void rgw_pool::from_str(const string& s)
2069{
1adf2230 2070 size_t pos = rgw_unescape_str(s, 0, '\\', ':', &name);
7c673cae 2071 if (pos != string::npos) {
1adf2230 2072 pos = rgw_unescape_str(s, pos, '\\', ':', &ns);
7c673cae
FG
2073 /* ignore return; if pos != string::npos it means that we had a colon
2074 * in the middle of ns that wasn't escaped, we're going to stop there
2075 */
2076 }
2077}
2078
2079string rgw_pool::to_str() const
2080{
2081 string esc_name;
1adf2230 2082 rgw_escape_str(name, '\\', ':', &esc_name);
7c673cae
FG
2083 if (ns.empty()) {
2084 return esc_name;
2085 }
2086 string esc_ns;
1adf2230 2087 rgw_escape_str(ns, '\\', ':', &esc_ns);
7c673cae
FG
2088 return esc_name + ":" + esc_ns;
2089}
2090
11fdf7f2 2091void rgw_raw_obj::decode_from_rgw_obj(bufferlist::const_iterator& bl)
7c673cae 2092{
11fdf7f2 2093 using ceph::decode;
7c673cae 2094 rgw_obj old_obj;
11fdf7f2 2095 decode(old_obj, bl);
7c673cae
FG
2096
2097 get_obj_bucket_and_oid_loc(old_obj, oid, loc);
2098 pool = old_obj.get_explicit_data_pool();
2099}
2100
7c673cae
FG
2101static struct rgw_name_to_flag op_type_mapping[] = { {"*", RGW_OP_TYPE_ALL},
2102 {"read", RGW_OP_TYPE_READ},
2103 {"write", RGW_OP_TYPE_WRITE},
2104 {"delete", RGW_OP_TYPE_DELETE},
2105 {NULL, 0} };
2106
2107
2108int rgw_parse_op_type_list(const string& str, uint32_t *perm)
2109{
9f95a23c 2110 return rgw_parse_list_of_flags(op_type_mapping, str, perm);
7c673cae
FG
2111}
2112
f67539c2 2113bool match_policy(std::string_view pattern, std::string_view input,
d2e6a577 2114 uint32_t flag)
7c673cae 2115{
d2e6a577
FG
2116 const uint32_t flag2 = flag & (MATCH_POLICY_ACTION|MATCH_POLICY_ARN) ?
2117 MATCH_CASE_INSENSITIVE : 0;
f64942e4
AA
2118 const bool colonblocks = !(flag & (MATCH_POLICY_RESOURCE |
2119 MATCH_POLICY_STRING));
7c673cae 2120
f67539c2
TL
2121 const auto npos = std::string_view::npos;
2122 std::string_view::size_type last_pos_input = 0, last_pos_pattern = 0;
d2e6a577 2123 while (true) {
f64942e4
AA
2124 auto cur_pos_input = colonblocks ? input.find(":", last_pos_input) : npos;
2125 auto cur_pos_pattern =
2126 colonblocks ? pattern.find(":", last_pos_pattern) : npos;
7c673cae 2127
d2e6a577
FG
2128 auto substr_input = input.substr(last_pos_input, cur_pos_input);
2129 auto substr_pattern = pattern.substr(last_pos_pattern, cur_pos_pattern);
7c673cae 2130
d2e6a577
FG
2131 if (!match_wildcards(substr_pattern, substr_input, flag2))
2132 return false;
2133
2134 if (cur_pos_pattern == npos)
2135 return cur_pos_input == npos;
2136 if (cur_pos_input == npos)
2137 return false;
7c673cae
FG
2138
2139 last_pos_pattern = cur_pos_pattern + 1;
2140 last_pos_input = cur_pos_input + 1;
2141 }
2142}
11fdf7f2
TL
2143
2144/*
2145 * make attrs look-like-this
2146 * converts underscores to dashes
2147 */
2148string lowercase_dash_http_attr(const string& orig)
2149{
2150 const char *s = orig.c_str();
2151 char buf[orig.size() + 1];
2152 buf[orig.size()] = '\0';
2153
2154 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2155 switch (*s) {
2156 case '_':
2157 buf[i] = '-';
2158 break;
2159 default:
2160 buf[i] = tolower(*s);
2161 }
2162 }
2163 return string(buf);
2164}
2165
2166/*
2167 * make attrs Look-Like-This
2168 * converts underscores to dashes
2169 */
2170string camelcase_dash_http_attr(const string& orig)
2171{
2172 const char *s = orig.c_str();
2173 char buf[orig.size() + 1];
2174 buf[orig.size()] = '\0';
2175
2176 bool last_sep = true;
2177
2178 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2179 switch (*s) {
2180 case '_':
2181 case '-':
2182 buf[i] = '-';
2183 last_sep = true;
2184 break;
2185 default:
2186 if (last_sep) {
2187 buf[i] = toupper(*s);
2188 } else {
2189 buf[i] = tolower(*s);
2190 }
2191 last_sep = false;
2192 }
2193 }
2194 return string(buf);
2195}
9f95a23c
TL
2196
2197RGWBucketInfo::RGWBucketInfo()
2198{
2199}
2200
2201RGWBucketInfo::~RGWBucketInfo()
2202{
2203}
2204
2205void RGWBucketInfo::encode(bufferlist& bl) const {
f67539c2 2206 ENCODE_START(23, 4, bl);
9f95a23c
TL
2207 encode(bucket, bl);
2208 encode(owner.id, bl);
2209 encode(flags, bl);
2210 encode(zonegroup, bl);
2211 uint64_t ct = real_clock::to_time_t(creation_time);
2212 encode(ct, bl);
2213 encode(placement_rule, bl);
2214 encode(has_instance_obj, bl);
2215 encode(quota, bl);
9f95a23c
TL
2216 encode(requester_pays, bl);
2217 encode(owner.tenant, bl);
2218 encode(has_website, bl);
2219 if (has_website) {
2220 encode(website_conf, bl);
2221 }
9f95a23c
TL
2222 encode(swift_versioning, bl);
2223 if (swift_versioning) {
2224 encode(swift_ver_location, bl);
2225 }
2226 encode(creation_time, bl);
2227 encode(mdsearch_config, bl);
2228 encode(reshard_status, bl);
2229 encode(new_bucket_instance_id, bl);
2230 if (obj_lock_enabled()) {
2231 encode(obj_lock, bl);
2232 }
2233 bool has_sync_policy = !empty_sync_policy();
2234 encode(has_sync_policy, bl);
2235 if (has_sync_policy) {
2236 encode(*sync_policy, bl);
2237 }
f67539c2
TL
2238 encode(layout, bl);
2239 encode(owner.ns, bl);
9f95a23c
TL
2240 ENCODE_FINISH(bl);
2241}
2242
2243void RGWBucketInfo::decode(bufferlist::const_iterator& bl) {
f67539c2 2244 DECODE_START_LEGACY_COMPAT_LEN_32(23, 4, 4, bl);
9f95a23c
TL
2245 decode(bucket, bl);
2246 if (struct_v >= 2) {
2247 string s;
2248 decode(s, bl);
2249 owner.from_str(s);
2250 }
2251 if (struct_v >= 3)
2252 decode(flags, bl);
2253 if (struct_v >= 5)
2254 decode(zonegroup, bl);
2255 if (struct_v >= 6) {
2256 uint64_t ct;
2257 decode(ct, bl);
2258 if (struct_v < 17)
2259 creation_time = ceph::real_clock::from_time_t((time_t)ct);
2260 }
2261 if (struct_v >= 7)
2262 decode(placement_rule, bl);
2263 if (struct_v >= 8)
2264 decode(has_instance_obj, bl);
2265 if (struct_v >= 9)
2266 decode(quota, bl);
f67539c2
TL
2267 static constexpr uint8_t new_layout_v = 22;
2268 if (struct_v >= 10 && struct_v < new_layout_v)
2269 decode(layout.current_index.layout.normal.num_shards, bl);
2270 if (struct_v >= 11 && struct_v < new_layout_v)
2271 decode(layout.current_index.layout.normal.hash_type, bl);
9f95a23c
TL
2272 if (struct_v >= 12)
2273 decode(requester_pays, bl);
2274 if (struct_v >= 13)
2275 decode(owner.tenant, bl);
2276 if (struct_v >= 14) {
2277 decode(has_website, bl);
2278 if (has_website) {
2279 decode(website_conf, bl);
2280 } else {
2281 website_conf = RGWBucketWebsiteConf();
2282 }
2283 }
f67539c2 2284 if (struct_v >= 15 && struct_v < new_layout_v) {
9f95a23c
TL
2285 uint32_t it;
2286 decode(it, bl);
f67539c2 2287 layout.current_index.layout.type = (rgw::BucketIndexType)it;
9f95a23c 2288 } else {
f67539c2 2289 layout.current_index.layout.type = rgw::BucketIndexType::Normal;
9f95a23c
TL
2290 }
2291 swift_versioning = false;
2292 swift_ver_location.clear();
2293 if (struct_v >= 16) {
2294 decode(swift_versioning, bl);
2295 if (swift_versioning) {
2296 decode(swift_ver_location, bl);
2297 }
2298 }
2299 if (struct_v >= 17) {
2300 decode(creation_time, bl);
2301 }
2302 if (struct_v >= 18) {
2303 decode(mdsearch_config, bl);
2304 }
2305 if (struct_v >= 19) {
2306 decode(reshard_status, bl);
2307 decode(new_bucket_instance_id, bl);
2308 }
2309 if (struct_v >= 20 && obj_lock_enabled()) {
2310 decode(obj_lock, bl);
2311 }
2312 if (struct_v >= 21) {
2313 decode(sync_policy, bl);
2314 }
f67539c2
TL
2315 if (struct_v >= 22) {
2316 decode(layout, bl);
2317 }
2318 if (struct_v >= 23) {
2319 decode(owner.ns, bl);
2320 }
a4b75251
TL
2321
2322 if (layout.logs.empty() &&
2323 layout.current_index.layout.type == rgw::BucketIndexType::Normal) {
2324 layout.logs.push_back(rgw::log_layout_from_index(0, layout.current_index.layout.normal));
2325 }
9f95a23c
TL
2326 DECODE_FINISH(bl);
2327}
2328
2329void RGWBucketInfo::set_sync_policy(rgw_sync_policy_info&& policy)
2330{
2331 sync_policy = std::move(policy);
2332}
2333
2334bool RGWBucketInfo::empty_sync_policy() const
2335{
2336 if (!sync_policy) {
2337 return true;
2338 }
2339
2340 return sync_policy->empty();
2341}
2342
20effc67
TL
2343struct rgw_pool;
2344struct rgw_placement_rule;
2345class RGWUserCaps;
2346
2347void decode_json_obj(rgw_pool& pool, JSONObj *obj)
2348{
2349 string s;
2350 decode_json_obj(s, obj);
2351 pool = rgw_pool(s);
2352}
2353
2354void encode_json(const char *name, const rgw_placement_rule& r, Formatter *f)
2355{
2356 encode_json(name, r.to_str(), f);
2357}
2358
2359void encode_json(const char *name, const rgw_pool& pool, Formatter *f)
2360{
2361 f->dump_string(name, pool.to_str());
2362}
2363
2364void encode_json(const char *name, const RGWUserCaps& val, Formatter *f)
2365{
2366 val.dump(f, name);
2367}
2368
2369void RGWBucketEnt::generate_test_instances(list<RGWBucketEnt*>& o)
2370{
2371 RGWBucketEnt *e = new RGWBucketEnt;
2372 init_bucket(&e->bucket, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2373 e->size = 1024;
2374 e->size_rounded = 4096;
2375 e->count = 1;
2376 o.push_back(e);
2377 o.push_back(new RGWBucketEnt);
2378}
2379
2380void RGWBucketEnt::dump(Formatter *f) const
2381{
2382 encode_json("bucket", bucket, f);
2383 encode_json("size", size, f);
2384 encode_json("size_rounded", size_rounded, f);
2385 utime_t ut(creation_time);
2386 encode_json("mtime", ut, f); /* mtime / creation time discrepency needed for backward compatibility */
2387 encode_json("count", count, f);
2388 encode_json("placement_rule", placement_rule.to_str(), f);
2389}
2390
2391void rgw_obj::generate_test_instances(list<rgw_obj*>& o)
2392{
2393 rgw_bucket b;
2394 init_bucket(&b, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2395 rgw_obj *obj = new rgw_obj(b, "object");
2396 o.push_back(obj);
2397 o.push_back(new rgw_obj);
2398}
2399
2400void rgw_bucket_placement::dump(Formatter *f) const
2401{
2402 encode_json("bucket", bucket, f);
2403 encode_json("placement_rule", placement_rule, f);
2404}
2405
2406void RGWBucketInfo::generate_test_instances(list<RGWBucketInfo*>& o)
2407{
2408 // Since things without a log will have one synthesized on decode,
2409 // ensure the things we attempt to encode will have one added so we
2410 // round-trip properly.
2411 auto gen_layout = [](rgw::BucketLayout& l) {
2412 l.current_index.gen = 0;
2413 l.current_index.layout.normal.hash_type = rgw::BucketHashType::Mod;
2414 l.current_index.layout.type = rgw::BucketIndexType::Normal;
2415 l.current_index.layout.normal.num_shards = 11;
2416 l.logs.push_back(log_layout_from_index(
2417 l.current_index.gen,
2418 l.current_index.layout.normal));
2419 };
2420
2421
2422 RGWBucketInfo *i = new RGWBucketInfo;
2423 init_bucket(&i->bucket, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2424 i->owner = "owner";
2425 i->flags = BUCKET_SUSPENDED;
2426 gen_layout(i->layout);
2427 o.push_back(i);
2428 i = new RGWBucketInfo;
2429 gen_layout(i->layout);
2430 o.push_back(i);
2431}
2432
2433void RGWBucketInfo::dump(Formatter *f) const
2434{
2435 encode_json("bucket", bucket, f);
2436 utime_t ut(creation_time);
2437 encode_json("creation_time", ut, f);
2438 encode_json("owner", owner.to_str(), f);
2439 encode_json("flags", flags, f);
2440 encode_json("zonegroup", zonegroup, f);
2441 encode_json("placement_rule", placement_rule, f);
2442 encode_json("has_instance_obj", has_instance_obj, f);
2443 encode_json("quota", quota, f);
2444 encode_json("num_shards", layout.current_index.layout.normal.num_shards, f);
2445 encode_json("bi_shard_hash_type", (uint32_t)layout.current_index.layout.normal.hash_type, f);
2446 encode_json("requester_pays", requester_pays, f);
2447 encode_json("has_website", has_website, f);
2448 if (has_website) {
2449 encode_json("website_conf", website_conf, f);
2450 }
2451 encode_json("swift_versioning", swift_versioning, f);
2452 encode_json("swift_ver_location", swift_ver_location, f);
2453 encode_json("index_type", (uint32_t)layout.current_index.layout.type, f);
2454 encode_json("mdsearch_config", mdsearch_config, f);
2455 encode_json("reshard_status", (int)reshard_status, f);
2456 encode_json("new_bucket_instance_id", new_bucket_instance_id, f);
2457 if (!empty_sync_policy()) {
2458 encode_json("sync_policy", *sync_policy, f);
2459 }
2460}
2461
2462void RGWBucketInfo::decode_json(JSONObj *obj) {
2463 JSONDecoder::decode_json("bucket", bucket, obj);
2464 utime_t ut;
2465 JSONDecoder::decode_json("creation_time", ut, obj);
2466 creation_time = ut.to_real_time();
2467 JSONDecoder::decode_json("owner", owner, obj);
2468 JSONDecoder::decode_json("flags", flags, obj);
2469 JSONDecoder::decode_json("zonegroup", zonegroup, obj);
2470 /* backward compatability with region */
2471 if (zonegroup.empty()) {
2472 JSONDecoder::decode_json("region", zonegroup, obj);
2473 }
2474 string pr;
2475 JSONDecoder::decode_json("placement_rule", pr, obj);
2476 placement_rule.from_str(pr);
2477 JSONDecoder::decode_json("has_instance_obj", has_instance_obj, obj);
2478 JSONDecoder::decode_json("quota", quota, obj);
2479 JSONDecoder::decode_json("num_shards", layout.current_index.layout.normal.num_shards, obj);
2480 uint32_t hash_type;
2481 JSONDecoder::decode_json("bi_shard_hash_type", hash_type, obj);
2482 layout.current_index.layout.normal.hash_type = static_cast<rgw::BucketHashType>(hash_type);
2483 JSONDecoder::decode_json("requester_pays", requester_pays, obj);
2484 JSONDecoder::decode_json("has_website", has_website, obj);
2485 if (has_website) {
2486 JSONDecoder::decode_json("website_conf", website_conf, obj);
2487 }
2488 JSONDecoder::decode_json("swift_versioning", swift_versioning, obj);
2489 JSONDecoder::decode_json("swift_ver_location", swift_ver_location, obj);
2490 uint32_t it;
2491 JSONDecoder::decode_json("index_type", it, obj);
2492 layout.current_index.layout.type = (rgw::BucketIndexType)it;
2493 JSONDecoder::decode_json("mdsearch_config", mdsearch_config, obj);
2494 int rs;
2495 JSONDecoder::decode_json("reshard_status", rs, obj);
2496 reshard_status = (cls_rgw_reshard_status)rs;
2497
2498 rgw_sync_policy_info sp;
2499 JSONDecoder::decode_json("sync_policy", sp, obj);
2500 if (!sp.empty()) {
2501 set_sync_policy(std::move(sp));
2502 }
2503}
2504
2505void RGWUserInfo::generate_test_instances(list<RGWUserInfo*>& o)
2506{
2507 RGWUserInfo *i = new RGWUserInfo;
2508 i->user_id = "user_id";
2509 i->display_name = "display_name";
2510 i->user_email = "user@email";
2511 RGWAccessKey k1, k2;
2512 k1.id = "id1";
2513 k1.key = "key1";
2514 k2.id = "id2";
2515 k2.subuser = "subuser";
2516 RGWSubUser u;
2517 u.name = "id2";
2518 u.perm_mask = 0x1;
2519 i->access_keys[k1.id] = k1;
2520 i->swift_keys[k2.id] = k2;
2521 i->subusers[u.name] = u;
2522 o.push_back(i);
2523
2524 o.push_back(new RGWUserInfo);
2525}
2526
2527static void user_info_dump_subuser(const char *name, const RGWSubUser& subuser, Formatter *f, void *parent)
2528{
2529 RGWUserInfo *info = static_cast<RGWUserInfo *>(parent);
2530 subuser.dump(f, info->user_id.to_str());
2531}
2532
2533static void user_info_dump_key(const char *name, const RGWAccessKey& key, Formatter *f, void *parent)
2534{
2535 RGWUserInfo *info = static_cast<RGWUserInfo *>(parent);
2536 key.dump(f, info->user_id.to_str(), false);
2537}
2538
2539static void user_info_dump_swift_key(const char *name, const RGWAccessKey& key, Formatter *f, void *parent)
2540{
2541 RGWUserInfo *info = static_cast<RGWUserInfo *>(parent);
2542 key.dump(f, info->user_id.to_str(), true);
2543}
2544
2545static void decode_access_keys(map<string, RGWAccessKey>& m, JSONObj *o)
2546{
2547 RGWAccessKey k;
2548 k.decode_json(o);
2549 m[k.id] = k;
2550}
2551
2552static void decode_swift_keys(map<string, RGWAccessKey>& m, JSONObj *o)
2553{
2554 RGWAccessKey k;
2555 k.decode_json(o, true);
2556 m[k.id] = k;
2557}
2558
2559static void decode_subusers(map<string, RGWSubUser>& m, JSONObj *o)
2560{
2561 RGWSubUser u;
2562 u.decode_json(o);
2563 m[u.name] = u;
2564}
2565
2566
2567struct rgw_flags_desc {
2568 uint32_t mask;
2569 const char *str;
2570};
2571
2572static struct rgw_flags_desc rgw_perms[] = {
2573 { RGW_PERM_FULL_CONTROL, "full-control" },
2574 { RGW_PERM_READ | RGW_PERM_WRITE, "read-write" },
2575 { RGW_PERM_READ, "read" },
2576 { RGW_PERM_WRITE, "write" },
2577 { RGW_PERM_READ_ACP, "read-acp" },
2578 { RGW_PERM_WRITE_ACP, "write-acp" },
2579 { 0, NULL }
2580};
2581
2582void rgw_perm_to_str(uint32_t mask, char *buf, int len)
2583{
2584 const char *sep = "";
2585 int pos = 0;
2586 if (!mask) {
2587 snprintf(buf, len, "<none>");
2588 return;
2589 }
2590 while (mask) {
2591 uint32_t orig_mask = mask;
2592 for (int i = 0; rgw_perms[i].mask; i++) {
2593 struct rgw_flags_desc *desc = &rgw_perms[i];
2594 if ((mask & desc->mask) == desc->mask) {
2595 pos += snprintf(buf + pos, len - pos, "%s%s", sep, desc->str);
2596 if (pos == len)
2597 return;
2598 sep = ", ";
2599 mask &= ~desc->mask;
2600 if (!mask)
2601 return;
2602 }
2603 }
2604 if (mask == orig_mask) // no change
2605 break;
2606 }
2607}
2608
2609uint32_t rgw_str_to_perm(const char *str)
2610{
2611 if (strcasecmp(str, "") == 0)
2612 return RGW_PERM_NONE;
2613 else if (strcasecmp(str, "read") == 0)
2614 return RGW_PERM_READ;
2615 else if (strcasecmp(str, "write") == 0)
2616 return RGW_PERM_WRITE;
2617 else if (strcasecmp(str, "readwrite") == 0)
2618 return RGW_PERM_READ | RGW_PERM_WRITE;
2619 else if (strcasecmp(str, "full") == 0)
2620 return RGW_PERM_FULL_CONTROL;
2621
2622 return RGW_PERM_INVALID;
2623}
2624
2625template <class T>
2626static void mask_to_str(T *mask_list, uint32_t mask, char *buf, int len)
2627{
2628 const char *sep = "";
2629 int pos = 0;
2630 if (!mask) {
2631 snprintf(buf, len, "<none>");
2632 return;
2633 }
2634 while (mask) {
2635 uint32_t orig_mask = mask;
2636 for (int i = 0; mask_list[i].mask; i++) {
2637 T *desc = &mask_list[i];
2638 if ((mask & desc->mask) == desc->mask) {
2639 pos += snprintf(buf + pos, len - pos, "%s%s", sep, desc->str);
2640 if (pos == len)
2641 return;
2642 sep = ", ";
2643 mask &= ~desc->mask;
2644 if (!mask)
2645 return;
2646 }
2647 }
2648 if (mask == orig_mask) // no change
2649 break;
2650 }
2651}
2652
2653static void perm_to_str(uint32_t mask, char *buf, int len)
2654{
2655 return mask_to_str(rgw_perms, mask, buf, len);
2656}
2657
2658static struct rgw_flags_desc op_type_flags[] = {
2659 { RGW_OP_TYPE_READ, "read" },
2660 { RGW_OP_TYPE_WRITE, "write" },
2661 { RGW_OP_TYPE_DELETE, "delete" },
2662 { 0, NULL }
2663};
2664
2665void op_type_to_str(uint32_t mask, char *buf, int len)
2666{
2667 return mask_to_str(op_type_flags, mask, buf, len);
2668}
2669
2670void RGWRateLimitInfo::decode_json(JSONObj *obj)
2671{
2672 JSONDecoder::decode_json("max_read_ops", max_read_ops, obj);
2673 JSONDecoder::decode_json("max_write_ops", max_write_ops, obj);
2674 JSONDecoder::decode_json("max_read_bytes", max_read_ops, obj);
2675 JSONDecoder::decode_json("max_write_bytes", max_write_ops, obj);
2676 JSONDecoder::decode_json("enabled", enabled, obj);
2677}
2678
2679void RGWRateLimitInfo::dump(Formatter *f) const
2680{
2681 f->dump_int("max_read_ops", max_read_ops);
2682 f->dump_int("max_write_ops", max_write_ops);
2683 f->dump_int("max_read_bytes", max_read_bytes);
2684 f->dump_int("max_write_bytes", max_write_bytes);
2685 f->dump_bool("enabled", enabled);
2686}
2687
2688void RGWUserInfo::dump(Formatter *f) const
2689{
2690
2691 encode_json("user_id", user_id.to_str(), f);
2692 encode_json("display_name", display_name, f);
2693 encode_json("email", user_email, f);
2694 encode_json("suspended", (int)suspended, f);
2695 encode_json("max_buckets", (int)max_buckets, f);
2696
2697 encode_json_map("subusers", NULL, "subuser", NULL, user_info_dump_subuser,(void *)this, subusers, f);
2698 encode_json_map("keys", NULL, "key", NULL, user_info_dump_key,(void *)this, access_keys, f);
2699 encode_json_map("swift_keys", NULL, "key", NULL, user_info_dump_swift_key,(void *)this, swift_keys, f);
2700
2701 encode_json("caps", caps, f);
2702
2703 char buf[256];
2704 op_type_to_str(op_mask, buf, sizeof(buf));
2705 encode_json("op_mask", (const char *)buf, f);
2706
2707 if (system) { /* no need to show it for every user */
2708 encode_json("system", (bool)system, f);
2709 }
2710 if (admin) {
2711 encode_json("admin", (bool)admin, f);
2712 }
2713 encode_json("default_placement", default_placement.name, f);
2714 encode_json("default_storage_class", default_placement.storage_class, f);
2715 encode_json("placement_tags", placement_tags, f);
2716 encode_json("bucket_quota", bucket_quota, f);
2717 encode_json("user_quota", user_quota, f);
2718 encode_json("temp_url_keys", temp_url_keys, f);
2719
2720 string user_source_type;
2721 switch ((RGWIdentityType)type) {
2722 case TYPE_RGW:
2723 user_source_type = "rgw";
2724 break;
2725 case TYPE_KEYSTONE:
2726 user_source_type = "keystone";
2727 break;
2728 case TYPE_LDAP:
2729 user_source_type = "ldap";
2730 break;
2731 case TYPE_NONE:
2732 user_source_type = "none";
2733 break;
2734 default:
2735 user_source_type = "none";
2736 break;
2737 }
2738 encode_json("type", user_source_type, f);
2739 encode_json("mfa_ids", mfa_ids, f);
2740}
2741
2742void RGWUserInfo::decode_json(JSONObj *obj)
2743{
2744 string uid;
2745
2746 JSONDecoder::decode_json("user_id", uid, obj, true);
2747 user_id.from_str(uid);
2748
2749 JSONDecoder::decode_json("display_name", display_name, obj);
2750 JSONDecoder::decode_json("email", user_email, obj);
2751 bool susp = false;
2752 JSONDecoder::decode_json("suspended", susp, obj);
2753 suspended = (__u8)susp;
2754 JSONDecoder::decode_json("max_buckets", max_buckets, obj);
2755
2756 JSONDecoder::decode_json("keys", access_keys, decode_access_keys, obj);
2757 JSONDecoder::decode_json("swift_keys", swift_keys, decode_swift_keys, obj);
2758 JSONDecoder::decode_json("subusers", subusers, decode_subusers, obj);
2759
2760 JSONDecoder::decode_json("caps", caps, obj);
2761
2762 string mask_str;
2763 JSONDecoder::decode_json("op_mask", mask_str, obj);
2764 rgw_parse_op_type_list(mask_str, &op_mask);
2765
2766 bool sys = false;
2767 JSONDecoder::decode_json("system", sys, obj);
2768 system = (__u8)sys;
2769 bool ad = false;
2770 JSONDecoder::decode_json("admin", ad, obj);
2771 admin = (__u8)ad;
2772 JSONDecoder::decode_json("default_placement", default_placement.name, obj);
2773 JSONDecoder::decode_json("default_storage_class", default_placement.storage_class, obj);
2774 JSONDecoder::decode_json("placement_tags", placement_tags, obj);
2775 JSONDecoder::decode_json("bucket_quota", bucket_quota, obj);
2776 JSONDecoder::decode_json("user_quota", user_quota, obj);
2777 JSONDecoder::decode_json("temp_url_keys", temp_url_keys, obj);
2778
2779 string user_source_type;
2780 JSONDecoder::decode_json("type", user_source_type, obj);
2781 if (user_source_type == "rgw") {
2782 type = TYPE_RGW;
2783 } else if (user_source_type == "keystone") {
2784 type = TYPE_KEYSTONE;
2785 } else if (user_source_type == "ldap") {
2786 type = TYPE_LDAP;
2787 } else if (user_source_type == "none") {
2788 type = TYPE_NONE;
2789 }
2790 JSONDecoder::decode_json("mfa_ids", mfa_ids, obj);
2791}
2792
2793
2794void RGWSubUser::generate_test_instances(list<RGWSubUser*>& o)
2795{
2796 RGWSubUser *u = new RGWSubUser;
2797 u->name = "name";
2798 u->perm_mask = 0xf;
2799 o.push_back(u);
2800 o.push_back(new RGWSubUser);
2801}
2802
2803void RGWSubUser::dump(Formatter *f) const
2804{
2805 encode_json("id", name, f);
2806 char buf[256];
2807 perm_to_str(perm_mask, buf, sizeof(buf));
2808 encode_json("permissions", (const char *)buf, f);
2809}
2810
2811void RGWSubUser::dump(Formatter *f, const string& user) const
2812{
2813 string s = user;
2814 s.append(":");
2815 s.append(name);
2816 encode_json("id", s, f);
2817 char buf[256];
2818 perm_to_str(perm_mask, buf, sizeof(buf));
2819 encode_json("permissions", (const char *)buf, f);
2820}
2821
2822uint32_t str_to_perm(const string& s)
2823{
2824 if (s.compare("read") == 0)
2825 return RGW_PERM_READ;
2826 else if (s.compare("write") == 0)
2827 return RGW_PERM_WRITE;
2828 else if (s.compare("read-write") == 0)
2829 return RGW_PERM_READ | RGW_PERM_WRITE;
2830 else if (s.compare("full-control") == 0)
2831 return RGW_PERM_FULL_CONTROL;
2832 return 0;
2833}
2834
2835void RGWSubUser::decode_json(JSONObj *obj)
2836{
2837 string uid;
2838 JSONDecoder::decode_json("id", uid, obj);
2839 int pos = uid.find(':');
2840 if (pos >= 0)
2841 name = uid.substr(pos + 1);
2842 string perm_str;
2843 JSONDecoder::decode_json("permissions", perm_str, obj);
2844 perm_mask = str_to_perm(perm_str);
2845}
2846
2847void RGWAccessKey::generate_test_instances(list<RGWAccessKey*>& o)
2848{
2849 RGWAccessKey *k = new RGWAccessKey;
2850 k->id = "id";
2851 k->key = "key";
2852 k->subuser = "subuser";
2853 o.push_back(k);
2854 o.push_back(new RGWAccessKey);
2855}
2856
2857void RGWAccessKey::dump(Formatter *f) const
2858{
2859 encode_json("access_key", id, f);
2860 encode_json("secret_key", key, f);
2861 encode_json("subuser", subuser, f);
2862}
2863
2864void RGWAccessKey::dump_plain(Formatter *f) const
2865{
2866 encode_json("access_key", id, f);
2867 encode_json("secret_key", key, f);
2868}
2869
2870void RGWAccessKey::dump(Formatter *f, const string& user, bool swift) const
2871{
2872 string u = user;
2873 if (!subuser.empty()) {
2874 u.append(":");
2875 u.append(subuser);
2876 }
2877 encode_json("user", u, f);
2878 if (!swift) {
2879 encode_json("access_key", id, f);
2880 }
2881 encode_json("secret_key", key, f);
2882}
2883
2884void RGWAccessKey::decode_json(JSONObj *obj) {
2885 JSONDecoder::decode_json("access_key", id, obj, true);
2886 JSONDecoder::decode_json("secret_key", key, obj, true);
2887 if (!JSONDecoder::decode_json("subuser", subuser, obj)) {
2888 string user;
2889 JSONDecoder::decode_json("user", user, obj);
2890 int pos = user.find(':');
2891 if (pos >= 0) {
2892 subuser = user.substr(pos + 1);
2893 }
2894 }
2895}
2896
2897void RGWAccessKey::decode_json(JSONObj *obj, bool swift) {
2898 if (!swift) {
2899 decode_json(obj);
2900 return;
2901 }
2902
2903 if (!JSONDecoder::decode_json("subuser", subuser, obj)) {
2904 JSONDecoder::decode_json("user", id, obj, true);
2905 int pos = id.find(':');
2906 if (pos >= 0) {
2907 subuser = id.substr(pos + 1);
2908 }
2909 }
2910 JSONDecoder::decode_json("secret_key", key, obj, true);
2911}
2912
2913void RGWStorageStats::dump(Formatter *f) const
2914{
2915 encode_json("size", size, f);
2916 encode_json("size_actual", size_rounded, f);
2917 if (dump_utilized) {
2918 encode_json("size_utilized", size_utilized, f);
2919 }
2920 encode_json("size_kb", rgw_rounded_kb(size), f);
2921 encode_json("size_kb_actual", rgw_rounded_kb(size_rounded), f);
2922 if (dump_utilized) {
2923 encode_json("size_kb_utilized", rgw_rounded_kb(size_utilized), f);
2924 }
2925 encode_json("num_objects", num_objects, f);
2926}
2927
2928void rgw_obj_key::dump(Formatter *f) const
2929{
2930 encode_json("name", name, f);
2931 encode_json("instance", instance, f);
2932 encode_json("ns", ns, f);
2933}
2934
2935void rgw_obj_key::decode_json(JSONObj *obj)
2936{
2937 JSONDecoder::decode_json("name", name, obj);
2938 JSONDecoder::decode_json("instance", instance, obj);
2939 JSONDecoder::decode_json("ns", ns, obj);
2940}
2941
2942void rgw_raw_obj::dump(Formatter *f) const
2943{
2944 encode_json("pool", pool, f);
2945 encode_json("oid", oid, f);
2946 encode_json("loc", loc, f);
2947}
2948
2949void rgw_raw_obj::decode_json(JSONObj *obj) {
2950 JSONDecoder::decode_json("pool", pool, obj);
2951 JSONDecoder::decode_json("oid", oid, obj);
2952 JSONDecoder::decode_json("loc", loc, obj);
2953}
2954
2955void rgw_obj::dump(Formatter *f) const
2956{
2957 encode_json("bucket", bucket, f);
2958 encode_json("key", key, f);
2959}
2960
2961