]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_common.cc
buildsys: change download over to reef release
[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
39ae355f
TL
871void RGWHTTPArgs::remove(const string& name)
872{
873 auto val_iter = val_map.find(name);
874 if (val_iter != std::end(val_map)) {
875 val_map.erase(val_iter);
876 }
877
878 auto sys_val_iter = sys_val_map.find(name);
879 if (sys_val_iter != std::end(sys_val_map)) {
880 sys_val_map.erase(sys_val_iter);
881 }
882
883 auto subres_iter = sub_resources.find(name);
884 if (subres_iter != std::end(sub_resources)) {
885 sub_resources.erase(subres_iter);
886 }
887}
888
7c673cae
FG
889void RGWHTTPArgs::append(const string& name, const string& val)
890{
891 if (name.compare(0, sizeof(RGW_SYS_PARAM_PREFIX) - 1, RGW_SYS_PARAM_PREFIX) == 0) {
892 sys_val_map[name] = val;
893 } else {
894 val_map[name] = val;
895 }
896
20effc67 897// when sub_resources exclusive by object are added, please remember to update obj_sub_resource in RGWHTTPArgs::exist_obj_excl_sub_resource().
7c673cae
FG
898 if ((name.compare("acl") == 0) ||
899 (name.compare("cors") == 0) ||
11fdf7f2 900 (name.compare("notification") == 0) ||
7c673cae
FG
901 (name.compare("location") == 0) ||
902 (name.compare("logging") == 0) ||
903 (name.compare("usage") == 0) ||
904 (name.compare("lifecycle") == 0) ||
905 (name.compare("delete") == 0) ||
906 (name.compare("uploads") == 0) ||
907 (name.compare("partNumber") == 0) ||
908 (name.compare("uploadId") == 0) ||
909 (name.compare("versionId") == 0) ||
910 (name.compare("start-date") == 0) ||
911 (name.compare("end-date") == 0) ||
912 (name.compare("versions") == 0) ||
913 (name.compare("versioning") == 0) ||
914 (name.compare("website") == 0) ||
915 (name.compare("requestPayment") == 0) ||
224ce89b 916 (name.compare("torrent") == 0) ||
11fdf7f2
TL
917 (name.compare("tagging") == 0) ||
918 (name.compare("append") == 0) ||
9f95a23c
TL
919 (name.compare("position") == 0) ||
920 (name.compare("policyStatus") == 0) ||
921 (name.compare("publicAccessBlock") == 0)) {
7c673cae
FG
922 sub_resources[name] = val;
923 } else if (name[0] == 'r') { // root of all evil
924 if ((name.compare("response-content-type") == 0) ||
925 (name.compare("response-content-language") == 0) ||
926 (name.compare("response-expires") == 0) ||
927 (name.compare("response-cache-control") == 0) ||
928 (name.compare("response-content-disposition") == 0) ||
929 (name.compare("response-content-encoding") == 0)) {
930 sub_resources[name] = val;
931 has_resp_modifier = true;
932 }
933 } else if ((name.compare("subuser") == 0) ||
934 (name.compare("key") == 0) ||
935 (name.compare("caps") == 0) ||
936 (name.compare("index") == 0) ||
937 (name.compare("policy") == 0) ||
938 (name.compare("quota") == 0) ||
11fdf7f2 939 (name.compare("list") == 0) ||
9f95a23c
TL
940 (name.compare("object") == 0) ||
941 (name.compare("sync") == 0)) {
7c673cae
FG
942 if (!admin_subresource_added) {
943 sub_resources[name] = "";
944 admin_subresource_added = true;
945 }
946 }
947}
948
949const string& RGWHTTPArgs::get(const string& name, bool *exists) const
950{
951 auto iter = val_map.find(name);
952 bool e = (iter != std::end(val_map));
953 if (exists)
954 *exists = e;
955 if (e)
956 return iter->second;
957 return empty_str;
958}
959
d2e6a577
FG
960boost::optional<const std::string&>
961RGWHTTPArgs::get_optional(const std::string& name) const
962{
963 bool exists;
964 const std::string& value = get(name, &exists);
965 if (exists) {
966 return value;
967 } else {
968 return boost::none;
969 }
970}
971
20effc67 972int RGWHTTPArgs::get_bool(const string& name, bool *val, bool *exists) const
7c673cae 973{
20effc67 974 map<string, string>::const_iterator iter;
7c673cae
FG
975 iter = val_map.find(name);
976 bool e = (iter != val_map.end());
977 if (exists)
978 *exists = e;
979
980 if (e) {
981 const char *s = iter->second.c_str();
982
983 if (strcasecmp(s, "false") == 0) {
984 *val = false;
985 } else if (strcasecmp(s, "true") == 0) {
986 *val = true;
987 } else {
988 return -EINVAL;
989 }
990 }
991
992 return 0;
993}
994
20effc67 995int RGWHTTPArgs::get_bool(const char *name, bool *val, bool *exists) const
7c673cae
FG
996{
997 string s(name);
998 return get_bool(s, val, exists);
999}
1000
20effc67 1001void RGWHTTPArgs::get_bool(const char *name, bool *val, bool def_val) const
7c673cae
FG
1002{
1003 bool exists = false;
1004 if ((get_bool(name, val, &exists) < 0) ||
1005 !exists) {
1006 *val = def_val;
1007 }
1008}
1009
20effc67 1010int RGWHTTPArgs::get_int(const char *name, int *val, int def_val) const
11fdf7f2
TL
1011{
1012 bool exists = false;
1013 string val_str;
1014 val_str = get(name, &exists);
1015 if (!exists) {
1016 *val = def_val;
1017 return 0;
1018 }
1019
1020 string err;
1021
1022 *val = (int)strict_strtol(val_str.c_str(), 10, &err);
1023 if (!err.empty()) {
1024 *val = def_val;
1025 return -EINVAL;
1026 }
1027 return 0;
1028}
1029
7c673cae
FG
1030string RGWHTTPArgs::sys_get(const string& name, bool * const exists) const
1031{
1032 const auto iter = sys_val_map.find(name);
1033 const bool e = (iter != sys_val_map.end());
1034
1035 if (exists) {
1036 *exists = e;
1037 }
1038
1039 return e ? iter->second : string();
1040}
1041
f64942e4
AA
1042bool rgw_transport_is_secure(CephContext *cct, const RGWEnv& env)
1043{
1044 const auto& m = env.get_map();
1045 // frontend connected with ssl
1046 if (m.count("SERVER_PORT_SECURE")) {
1047 return true;
1048 }
1049 // ignore proxy headers unless explicitly enabled
1050 if (!cct->_conf->rgw_trust_forwarded_https) {
1051 return false;
1052 }
1053 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
1054 // Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>
1055 auto i = m.find("HTTP_FORWARDED");
1056 if (i != m.end() && i->second.find("proto=https") != std::string::npos) {
1057 return true;
1058 }
1059 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
1060 i = m.find("HTTP_X_FORWARDED_PROTO");
1061 if (i != m.end() && i->second == "https") {
1062 return true;
1063 }
1064 return false;
1065}
1066
9f95a23c 1067
11fdf7f2 1068namespace {
9f95a23c
TL
1069
1070struct perm_state_from_req_state : public perm_state_base {
1071 req_state * const s;
f67539c2
TL
1072 perm_state_from_req_state(req_state * const _s)
1073 : perm_state_base(_s->cct,
1074 _s->env,
1075 _s->auth.identity.get(),
1076 _s->bucket.get() ? _s->bucket->get_info() : RGWBucketInfo(),
1077 _s->perm_mask,
1078 _s->defer_to_bucket_acls,
1079 _s->bucket_access_conf),
1080 s(_s) {}
1081
9f95a23c
TL
1082 std::optional<bool> get_request_payer() const override {
1083 const char *request_payer = s->info.env->get("HTTP_X_AMZ_REQUEST_PAYER");
1084 if (!request_payer) {
1085 bool exists;
1086 request_payer = s->info.args.get("x-amz-request-payer", &exists).c_str();
1087 if (!exists) {
1088 return false;
1089 }
1090 }
1091
1092 if (strcasecmp(request_payer, "requester") == 0) {
1093 return true;
1094 }
1095
1096 return std::nullopt;
1097 }
1098
1099 const char *get_referer() const override {
1100 return s->info.env->get("HTTP_REFERER");
1101 }
1102};
1103
11fdf7f2
TL
1104Effect eval_or_pass(const boost::optional<Policy>& policy,
1105 const rgw::IAM::Environment& env,
1106 boost::optional<const rgw::auth::Identity&> id,
1107 const uint64_t op,
20effc67 1108 const ARN& resource,
522d829b 1109 boost::optional<rgw::IAM::PolicyPrincipal&> princ_type=boost::none) {
11fdf7f2
TL
1110 if (!policy)
1111 return Effect::Pass;
1112 else
20effc67 1113 return policy->eval(env, id, op, resource, princ_type);
11fdf7f2
TL
1114}
1115
1116}
1117
522d829b 1118Effect eval_identity_or_session_policies(const vector<Policy>& policies,
11fdf7f2 1119 const rgw::IAM::Environment& env,
11fdf7f2
TL
1120 const uint64_t op,
1121 const ARN& arn) {
522d829b
TL
1122 auto policy_res = Effect::Pass, prev_res = Effect::Pass;
1123 for (auto& policy : policies) {
20effc67 1124 if (policy_res = eval_or_pass(policy, env, boost::none, op, arn); policy_res == Effect::Deny)
522d829b
TL
1125 return policy_res;
1126 else if (policy_res == Effect::Allow)
11fdf7f2 1127 prev_res = Effect::Allow;
522d829b
TL
1128 else if (policy_res == Effect::Pass && prev_res == Effect::Allow)
1129 policy_res = Effect::Allow;
11fdf7f2 1130 }
522d829b 1131 return policy_res;
11fdf7f2
TL
1132}
1133
1134bool verify_user_permission(const DoutPrefixProvider* dpp,
9f95a23c 1135 perm_state_base * const s,
11fdf7f2
TL
1136 RGWAccessControlPolicy * const user_acl,
1137 const vector<rgw::IAM::Policy>& user_policies,
20effc67 1138 const vector<rgw::IAM::Policy>& session_policies,
eafe8130 1139 const rgw::ARN& res,
11fdf7f2
TL
1140 const uint64_t op)
1141{
20effc67
TL
1142 auto identity_policy_res = eval_identity_or_session_policies(user_policies, s->env, op, res);
1143 if (identity_policy_res == Effect::Deny) {
1144 return false;
1145 }
1146
1147 if (! session_policies.empty()) {
1148 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, op, res);
1149 if (session_policy_res == Effect::Deny) {
1150 return false;
1151 }
1152 //Intersection of identity policies and session policies
1153 if (identity_policy_res == Effect::Allow && session_policy_res == Effect::Allow) {
1154 return true;
1155 }
11fdf7f2
TL
1156 return false;
1157 }
1158
20effc67 1159 if (identity_policy_res == Effect::Allow) {
11fdf7f2
TL
1160 return true;
1161 }
1162
1163 if (op == rgw::IAM::s3CreateBucket || op == rgw::IAM::s3ListAllMyBuckets) {
1164 auto perm = op_to_perm(op);
1165
1166 return verify_user_permission_no_policy(dpp, s, user_acl, perm);
1167 }
1168
1169 return false;
1170}
1171
9f95a23c
TL
1172bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1173 struct perm_state_base * const s,
1174 RGWAccessControlPolicy * const user_acl,
1175 const int perm)
7c673cae 1176{
9f95a23c 1177 if (s->identity->get_identity_type() == TYPE_ROLE)
11fdf7f2
TL
1178 return false;
1179
7c673cae
FG
1180 /* S3 doesn't support account ACLs. */
1181 if (!user_acl)
1182 return true;
1183
1184 if ((perm & (int)s->perm_mask) != perm)
1185 return false;
1186
9f95a23c 1187 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
7c673cae
FG
1188}
1189
11fdf7f2
TL
1190bool verify_user_permission(const DoutPrefixProvider* dpp,
1191 struct req_state * const s,
eafe8130 1192 const rgw::ARN& res,
11fdf7f2
TL
1193 const uint64_t op)
1194{
9f95a23c 1195 perm_state_from_req_state ps(s);
20effc67 1196 return verify_user_permission(dpp, &ps, s->user_acl.get(), s->iam_user_policies, s->session_policies, res, op);
11fdf7f2
TL
1197}
1198
1199bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1200 struct req_state * const s,
1201 const int perm)
7c673cae 1202{
9f95a23c
TL
1203 perm_state_from_req_state ps(s);
1204 return verify_user_permission_no_policy(dpp, &ps, s->user_acl.get(), perm);
7c673cae
FG
1205}
1206
9f95a23c 1207bool verify_requester_payer_permission(struct perm_state_base *s)
7c673cae
FG
1208{
1209 if (!s->bucket_info.requester_pays)
1210 return true;
1211
9f95a23c 1212 if (s->identity->is_owner_of(s->bucket_info.owner))
7c673cae
FG
1213 return true;
1214
9f95a23c 1215 if (s->identity->is_anonymous()) {
7c673cae
FG
1216 return false;
1217 }
1218
9f95a23c
TL
1219 auto request_payer = s->get_request_payer();
1220 if (request_payer) {
1221 return *request_payer;
7c673cae
FG
1222 }
1223
1224 return false;
1225}
1226
11fdf7f2 1227bool verify_bucket_permission(const DoutPrefixProvider* dpp,
9f95a23c 1228 struct perm_state_base * const s,
31f18b77 1229 const rgw_bucket& bucket,
7c673cae
FG
1230 RGWAccessControlPolicy * const user_acl,
1231 RGWAccessControlPolicy * const bucket_acl,
11fdf7f2 1232 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1233 const vector<Policy>& identity_policies,
1234 const vector<Policy>& session_policies,
31f18b77 1235 const uint64_t op)
7c673cae 1236{
31f18b77 1237 if (!verify_requester_payer_permission(s))
7c673cae
FG
1238 return false;
1239
20effc67 1240 auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, op, ARN(bucket));
522d829b 1241 if (identity_policy_res == Effect::Deny)
11fdf7f2
TL
1242 return false;
1243
522d829b 1244 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
9f95a23c 1245 auto r = eval_or_pass(bucket_policy, s->env, *s->identity,
522d829b
TL
1246 op, ARN(bucket), princ_type);
1247 if (r == Effect::Deny)
1248 return false;
1249
1250 //Take into account session policies, if the identity making a request is a role
1251 if (!session_policies.empty()) {
20effc67 1252 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, op, ARN(bucket));
522d829b
TL
1253 if (session_policy_res == Effect::Deny) {
1254 return false;
1255 }
1256 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1257 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1258 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1259 (session_policy_res == Effect::Allow && r == Effect::Allow))
1260 return true;
1261 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1262 //Intersection of session policy and identity policy plus bucket policy
1263 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow)
1264 return true;
1265 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1266 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1267 return true;
1268 }
1269 return false;
1270 }
1271
1272 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
b32b8144
FG
1273 // It looks like S3 ACLs only GRANT permissions rather than
1274 // denying them, so this should be safe.
1275 return true;
31f18b77
FG
1276
1277 const auto perm = op_to_perm(op);
1278
11fdf7f2 1279 return verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm);
31f18b77
FG
1280}
1281
9f95a23c
TL
1282bool verify_bucket_permission(const DoutPrefixProvider* dpp,
1283 struct req_state * const s,
1284 const rgw_bucket& bucket,
1285 RGWAccessControlPolicy * const user_acl,
1286 RGWAccessControlPolicy * const bucket_acl,
1287 const boost::optional<Policy>& bucket_policy,
1288 const vector<Policy>& user_policies,
522d829b 1289 const vector<Policy>& session_policies,
9f95a23c
TL
1290 const uint64_t op)
1291{
1292 perm_state_from_req_state ps(s);
1293 return verify_bucket_permission(dpp, &ps, bucket,
1294 user_acl, bucket_acl,
1295 bucket_policy, user_policies,
522d829b 1296 session_policies, op);
9f95a23c
TL
1297}
1298
1299bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
31f18b77
FG
1300 RGWAccessControlPolicy * const user_acl,
1301 RGWAccessControlPolicy * const bucket_acl,
1302 const int perm)
1303{
1304 if (!bucket_acl)
7c673cae
FG
1305 return false;
1306
31f18b77 1307 if ((perm & (int)s->perm_mask) != perm)
7c673cae
FG
1308 return false;
1309
9f95a23c
TL
1310 if (bucket_acl->verify_permission(dpp, *s->identity, perm, perm,
1311 s->get_referer(),
1312 s->bucket_access_conf &&
1313 s->bucket_access_conf->ignore_public_acls()))
7c673cae
FG
1314 return true;
1315
1316 if (!user_acl)
1317 return false;
1318
9f95a23c
TL
1319 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
1320}
1321
1322bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s,
1323 RGWAccessControlPolicy * const user_acl,
1324 RGWAccessControlPolicy * const bucket_acl,
1325 const int perm)
1326{
1327 perm_state_from_req_state ps(s);
1328 return verify_bucket_permission_no_policy(dpp,
1329 &ps,
1330 user_acl,
1331 bucket_acl,
1332 perm);
7c673cae
FG
1333}
1334
11fdf7f2 1335bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s, const int perm)
31f18b77 1336{
9f95a23c
TL
1337 perm_state_from_req_state ps(s);
1338
1339 if (!verify_requester_payer_permission(&ps))
31f18b77
FG
1340 return false;
1341
11fdf7f2 1342 return verify_bucket_permission_no_policy(dpp,
9f95a23c 1343 &ps,
c07f9fc5
FG
1344 s->user_acl.get(),
1345 s->bucket_acl.get(),
1346 perm);
31f18b77
FG
1347}
1348
11fdf7f2 1349bool verify_bucket_permission(const DoutPrefixProvider* dpp, struct req_state * const s, const uint64_t op)
7c673cae 1350{
2a845540
TL
1351 if (rgw::sal::Bucket::empty(s->bucket)) {
1352 // request is missing a bucket name
1353 return false;
1354 }
1355
9f95a23c
TL
1356 perm_state_from_req_state ps(s);
1357
11fdf7f2 1358 return verify_bucket_permission(dpp,
9f95a23c 1359 &ps,
f67539c2 1360 s->bucket->get_key(),
7c673cae 1361 s->user_acl.get(),
c07f9fc5
FG
1362 s->bucket_acl.get(),
1363 s->iam_policy,
11fdf7f2 1364 s->iam_user_policies,
522d829b 1365 s->session_policies,
31f18b77
FG
1366 op);
1367}
1368
522d829b 1369// Authorize anyone permitted by the bucket policy, identity policies, session policies and the bucket owner
b32b8144
FG
1370// unless explicitly denied by the policy.
1371
1372int verify_bucket_owner_or_policy(struct req_state* const s,
1373 const uint64_t op)
1374{
20effc67 1375 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, op, ARN(s->bucket->get_key()));
522d829b 1376 if (identity_policy_res == Effect::Deny) {
f91f0fd5
TL
1377 return -EACCES;
1378 }
1379
522d829b 1380 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
b32b8144
FG
1381 auto e = eval_or_pass(s->iam_policy,
1382 s->env, *s->auth.identity,
522d829b 1383 op, ARN(s->bucket->get_key()), princ_type);
f91f0fd5
TL
1384 if (e == Effect::Deny) {
1385 return -EACCES;
1386 }
1387
522d829b 1388 if (!s->session_policies.empty()) {
20effc67 1389 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, op, ARN(s->bucket->get_key()));
522d829b
TL
1390 if (session_policy_res == Effect::Deny) {
1391 return -EACCES;
1392 }
1393 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1394 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1395 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1396 (session_policy_res == Effect::Allow && e == Effect::Allow))
1397 return 0;
1398 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1399 //Intersection of session policy and identity policy plus bucket policy
1400 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow)
1401 return 0;
1402 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1403 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1404 return 0;
1405 }
1406 return -EACCES;
1407 }
1408
b32b8144 1409 if (e == Effect::Allow ||
522d829b 1410 identity_policy_res == Effect::Allow ||
b32b8144 1411 (e == Effect::Pass &&
522d829b 1412 identity_policy_res == Effect::Pass &&
b32b8144
FG
1413 s->auth.identity->is_owner_of(s->bucket_owner.get_id()))) {
1414 return 0;
1415 } else {
1416 return -EACCES;
1417 }
1418}
1419
1420
11fdf7f2 1421static inline bool check_deferred_bucket_perms(const DoutPrefixProvider* dpp,
9f95a23c 1422 struct perm_state_base * const s,
31f18b77
FG
1423 const rgw_bucket& bucket,
1424 RGWAccessControlPolicy * const user_acl,
1425 RGWAccessControlPolicy * const bucket_acl,
11fdf7f2 1426 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1427 const vector<Policy>& identity_policies,
1428 const vector<Policy>& session_policies,
31f18b77
FG
1429 const uint8_t deferred_check,
1430 const uint64_t op)
1431{
1432 return (s->defer_to_bucket_acls == deferred_check \
522d829b 1433 && verify_bucket_permission(dpp, s, bucket, user_acl, bucket_acl, bucket_policy, identity_policies, session_policies,op));
7c673cae
FG
1434}
1435
11fdf7f2 1436static inline bool check_deferred_bucket_only_acl(const DoutPrefixProvider* dpp,
9f95a23c 1437 struct perm_state_base * const s,
31f18b77
FG
1438 RGWAccessControlPolicy * const user_acl,
1439 RGWAccessControlPolicy * const bucket_acl,
1440 const uint8_t deferred_check,
1441 const int perm)
7c673cae
FG
1442{
1443 return (s->defer_to_bucket_acls == deferred_check \
11fdf7f2 1444 && verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm));
7c673cae
FG
1445}
1446
9f95a23c 1447bool verify_object_permission(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
31f18b77 1448 const rgw_obj& obj,
7c673cae
FG
1449 RGWAccessControlPolicy * const user_acl,
1450 RGWAccessControlPolicy * const bucket_acl,
1451 RGWAccessControlPolicy * const object_acl,
11fdf7f2 1452 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1453 const vector<Policy>& identity_policies,
1454 const vector<Policy>& session_policies,
31f18b77 1455 const uint64_t op)
7c673cae
FG
1456{
1457 if (!verify_requester_payer_permission(s))
1458 return false;
1459
20effc67 1460 auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, op, ARN(obj));
522d829b 1461 if (identity_policy_res == Effect::Deny)
11fdf7f2 1462 return false;
b32b8144 1463
522d829b
TL
1464 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
1465 auto r = eval_or_pass(bucket_policy, s->env, *s->identity, op, ARN(obj), princ_type);
1466 if (r == Effect::Deny)
1467 return false;
1468
1469 if (!session_policies.empty()) {
20effc67 1470 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, op, ARN(obj));
522d829b
TL
1471 if (session_policy_res == Effect::Deny) {
1472 return false;
1473 }
1474 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1475 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1476 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1477 (session_policy_res == Effect::Allow && r == Effect::Allow))
1478 return true;
1479 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1480 //Intersection of session policy and identity policy plus bucket policy
1481 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow)
1482 return true;
1483 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1484 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1485 return true;
1486 }
1487 return false;
1488 }
1489
1490 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
b32b8144
FG
1491 // It looks like S3 ACLs only GRANT permissions rather than
1492 // denying them, so this should be safe.
1493 return true;
31f18b77
FG
1494
1495 const auto perm = op_to_perm(op);
1496
11fdf7f2 1497 if (check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
522d829b 1498 identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
11fdf7f2 1499 check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
522d829b 1500 identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
31f18b77
FG
1501 return true;
1502 }
1503
1504 if (!object_acl) {
1505 return false;
1506 }
1507
9f95a23c
TL
1508 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1509 nullptr, /* http_referrer */
1510 s->bucket_access_conf &&
1511 s->bucket_access_conf->ignore_public_acls());
31f18b77
FG
1512 if (ret) {
1513 return true;
1514 }
1515
1516 if (!s->cct->_conf->rgw_enforce_swift_acls)
1517 return ret;
1518
1519 if ((perm & (int)s->perm_mask) != perm)
1520 return false;
1521
1522 int swift_perm = 0;
1523 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1524 swift_perm |= RGW_PERM_READ_OBJS;
1525 if (perm & RGW_PERM_WRITE)
1526 swift_perm |= RGW_PERM_WRITE_OBJS;
1527
1528 if (!swift_perm)
1529 return false;
1530
1531 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1532 otherwise the mask might not cover the swift permissions bits */
9f95a23c
TL
1533 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1534 s->get_referer()))
31f18b77
FG
1535 return true;
1536
1537 if (!user_acl)
1538 return false;
1539
9f95a23c
TL
1540 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
1541}
1542
1543bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state * const s,
1544 const rgw_obj& obj,
1545 RGWAccessControlPolicy * const user_acl,
1546 RGWAccessControlPolicy * const bucket_acl,
1547 RGWAccessControlPolicy * const object_acl,
1548 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1549 const vector<Policy>& identity_policies,
1550 const vector<Policy>& session_policies,
9f95a23c
TL
1551 const uint64_t op)
1552{
1553 perm_state_from_req_state ps(s);
1554 return verify_object_permission(dpp, &ps, obj,
1555 user_acl, bucket_acl,
1556 object_acl, bucket_policy,
522d829b 1557 identity_policies, session_policies, op);
31f18b77
FG
1558}
1559
11fdf7f2 1560bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp,
9f95a23c 1561 struct perm_state_base * const s,
31f18b77
FG
1562 RGWAccessControlPolicy * const user_acl,
1563 RGWAccessControlPolicy * const bucket_acl,
1564 RGWAccessControlPolicy * const object_acl,
1565 const int perm)
1566{
11fdf7f2
TL
1567 if (check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, perm) ||
1568 check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, RGW_PERM_FULL_CONTROL)) {
7c673cae
FG
1569 return true;
1570 }
1571
1572 if (!object_acl) {
1573 return false;
1574 }
1575
9f95a23c
TL
1576 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1577 nullptr, /* http referrer */
1578 s->bucket_access_conf &&
1579 s->bucket_access_conf->ignore_public_acls());
7c673cae
FG
1580 if (ret) {
1581 return true;
1582 }
1583
1584 if (!s->cct->_conf->rgw_enforce_swift_acls)
1585 return ret;
1586
1587 if ((perm & (int)s->perm_mask) != perm)
1588 return false;
1589
1590 int swift_perm = 0;
1591 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1592 swift_perm |= RGW_PERM_READ_OBJS;
1593 if (perm & RGW_PERM_WRITE)
1594 swift_perm |= RGW_PERM_WRITE_OBJS;
1595
1596 if (!swift_perm)
1597 return false;
1598
1599 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1600 otherwise the mask might not cover the swift permissions bits */
9f95a23c
TL
1601 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1602 s->get_referer()))
7c673cae
FG
1603 return true;
1604
1605 if (!user_acl)
1606 return false;
1607
9f95a23c 1608 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
7c673cae
FG
1609}
1610
11fdf7f2 1611bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state *s, int perm)
31f18b77 1612{
9f95a23c
TL
1613 perm_state_from_req_state ps(s);
1614
1615 if (!verify_requester_payer_permission(&ps))
31f18b77
FG
1616 return false;
1617
11fdf7f2 1618 return verify_object_permission_no_policy(dpp,
9f95a23c 1619 &ps,
c07f9fc5
FG
1620 s->user_acl.get(),
1621 s->bucket_acl.get(),
1622 s->object_acl.get(),
1623 perm);
31f18b77
FG
1624}
1625
11fdf7f2 1626bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s, uint64_t op)
7c673cae 1627{
9f95a23c
TL
1628 perm_state_from_req_state ps(s);
1629
11fdf7f2 1630 return verify_object_permission(dpp,
9f95a23c 1631 &ps,
f67539c2 1632 rgw_obj(s->bucket->get_key(), s->object->get_key()),
c07f9fc5
FG
1633 s->user_acl.get(),
1634 s->bucket_acl.get(),
1635 s->object_acl.get(),
1636 s->iam_policy,
11fdf7f2 1637 s->iam_user_policies,
522d829b 1638 s->session_policies,
31f18b77 1639 op);
7c673cae
FG
1640}
1641
f67539c2 1642
20effc67 1643int verify_object_lock(const DoutPrefixProvider* dpp, const rgw::sal::Attrs& attrs, const bool bypass_perm, const bool bypass_governance_mode) {
f67539c2
TL
1644 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
1645 if (aiter != attrs.end()) {
1646 RGWObjectRetention obj_retention;
1647 try {
1648 decode(obj_retention, aiter->second);
1649 } catch (buffer::error& err) {
1650 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
1651 return -EIO;
1652 }
1653 if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) > ceph_clock_now()) {
1654 if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
1655 return -EACCES;
1656 }
1657 }
1658 }
1659 aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD);
1660 if (aiter != attrs.end()) {
1661 RGWObjectLegalHold obj_legal_hold;
1662 try {
1663 decode(obj_legal_hold, aiter->second);
1664 } catch (buffer::error& err) {
1665 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl;
1666 return -EIO;
1667 }
1668 if (obj_legal_hold.is_enabled()) {
1669 return -EACCES;
1670 }
1671 }
1672
1673 return 0;
1674}
1675
1676
7c673cae
FG
1677class HexTable
1678{
1679 char table[256];
1680
1681public:
1682 HexTable() {
92f5a8d4 1683 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
1684 memset(table, -1, sizeof(table));
1685 int i;
1686 for (i = '0'; i<='9'; i++)
1687 table[i] = i - '0';
1688 for (i = 'A'; i<='F'; i++)
1689 table[i] = i - 'A' + 0xa;
1690 for (i = 'a'; i<='f'; i++)
1691 table[i] = i - 'a' + 0xa;
1692 }
1693
1694 char to_num(char c) {
1695 return table[(int)c];
1696 }
1697};
1698
1699static char hex_to_num(char c)
1700{
1701 static HexTable hex_table;
1702 return hex_table.to_num(c);
1703}
1704
f67539c2 1705std::string url_decode(const std::string_view& src_str, bool in_query)
7c673cae 1706{
31f18b77
FG
1707 std::string dest_str;
1708 dest_str.reserve(src_str.length() + 1);
7c673cae 1709
31f18b77 1710 for (auto src = std::begin(src_str); src != std::end(src_str); ++src) {
7c673cae
FG
1711 if (*src != '%') {
1712 if (!in_query || *src != '+') {
31f18b77
FG
1713 if (*src == '?') {
1714 in_query = true;
1715 }
1716 dest_str.push_back(*src);
7c673cae 1717 } else {
31f18b77 1718 dest_str.push_back(' ');
7c673cae
FG
1719 }
1720 } else {
31f18b77
FG
1721 /* 3 == strlen("%%XX") */
1722 if (std::distance(src, std::end(src_str)) < 3) {
7c673cae 1723 break;
31f18b77
FG
1724 }
1725
1726 src++;
1727 const char c1 = hex_to_num(*src++);
1728 const char c2 = hex_to_num(*src);
1729 if (c1 < 0 || c2 < 0) {
1730 return std::string();
1731 } else {
1732 dest_str.push_back(c1 << 4 | c2);
1733 }
7c673cae
FG
1734 }
1735 }
7c673cae 1736
31f18b77 1737 return dest_str;
7c673cae
FG
1738}
1739
1740void rgw_uri_escape_char(char c, string& dst)
1741{
1742 char buf[16];
1743 snprintf(buf, sizeof(buf), "%%%.2X", (int)(unsigned char)c);
1744 dst.append(buf);
1745}
1746
1747static bool char_needs_url_encoding(char c)
1748{
1749 if (c <= 0x20 || c >= 0x7f)
1750 return true;
1751
1752 switch (c) {
1753 case 0x22:
1754 case 0x23:
1755 case 0x25:
1756 case 0x26:
1757 case 0x2B:
1758 case 0x2C:
1759 case 0x2F:
1760 case 0x3A:
1761 case 0x3B:
1762 case 0x3C:
1763 case 0x3E:
1764 case 0x3D:
1765 case 0x3F:
1766 case 0x40:
1767 case 0x5B:
1768 case 0x5D:
1769 case 0x5C:
1770 case 0x5E:
1771 case 0x60:
1772 case 0x7B:
1773 case 0x7D:
1774 return true;
1775 }
1776 return false;
1777}
1778
11fdf7f2 1779void url_encode(const string& src, string& dst, bool encode_slash)
7c673cae
FG
1780{
1781 const char *p = src.c_str();
1782 for (unsigned i = 0; i < src.size(); i++, p++) {
11fdf7f2
TL
1783 if ((!encode_slash && *p == 0x2F) || !char_needs_url_encoding(*p)) {
1784 dst.append(p, 1);
1785 }else {
7c673cae 1786 rgw_uri_escape_char(*p, dst);
7c673cae 1787 }
7c673cae
FG
1788 }
1789}
1790
11fdf7f2 1791std::string url_encode(const std::string& src, bool encode_slash)
7c673cae
FG
1792{
1793 std::string dst;
11fdf7f2 1794 url_encode(src, dst, encode_slash);
7c673cae
FG
1795
1796 return dst;
1797}
1798
f91f0fd5
TL
1799std::string url_remove_prefix(const std::string& url)
1800{
1801 std::string dst = url;
1802 auto pos = dst.find("http://");
1803 if (pos == std::string::npos) {
1804 pos = dst.find("https://");
1805 if (pos != std::string::npos) {
1806 dst.erase(pos, 8);
1807 } else {
1808 pos = dst.find("www.");
1809 if (pos != std::string::npos) {
1810 dst.erase(pos, 4);
1811 }
1812 }
1813 } else {
1814 dst.erase(pos, 7);
1815 }
1816
1817 return dst;
1818}
1819
7c673cae
FG
1820string rgw_trim_whitespace(const string& src)
1821{
1822 if (src.empty()) {
1823 return string();
1824 }
1825
1826 int start = 0;
1827 for (; start != (int)src.size(); start++) {
1828 if (!isspace(src[start]))
1829 break;
1830 }
1831
1832 int end = src.size() - 1;
1833 if (end < start) {
1834 return string();
1835 }
1836
1837 for (; end > start; end--) {
1838 if (!isspace(src[end]))
1839 break;
1840 }
1841
1842 return src.substr(start, end - start + 1);
1843}
1844
f67539c2 1845std::string_view rgw_trim_whitespace(const std::string_view& src)
7c673cae 1846{
f67539c2 1847 std::string_view res = src;
7c673cae
FG
1848
1849 while (res.size() > 0 && std::isspace(res.front())) {
1850 res.remove_prefix(1);
1851 }
1852 while (res.size() > 0 && std::isspace(res.back())) {
1853 res.remove_suffix(1);
1854 }
1855 return res;
1856}
1857
1858string rgw_trim_quotes(const string& val)
1859{
1860 string s = rgw_trim_whitespace(val);
1861 if (s.size() < 2)
1862 return s;
1863
1864 int start = 0;
1865 int end = s.size() - 1;
1866 int quotes_count = 0;
1867
1868 if (s[start] == '"') {
1869 start++;
1870 quotes_count++;
1871 }
1872 if (s[end] == '"') {
1873 end--;
1874 quotes_count++;
1875 }
1876 if (quotes_count == 2) {
1877 return s.substr(start, end - start + 1);
1878 }
1879 return s;
1880}
1881
7c673cae
FG
1882static struct rgw_name_to_flag cap_names[] = { {"*", RGW_CAP_ALL},
1883 {"read", RGW_CAP_READ},
1884 {"write", RGW_CAP_WRITE},
1885 {NULL, 0} };
1886
1887int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
1888{
9f95a23c 1889 return rgw_parse_list_of_flags(cap_names, str, perm);
7c673cae
FG
1890}
1891
1892int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm)
1893{
1894 int pos = cap.find('=');
1895 if (pos >= 0) {
31f18b77 1896 type = rgw_trim_whitespace(cap.substr(0, pos));
7c673cae
FG
1897 }
1898
1899 if (!is_valid_cap_type(type))
1900 return -ERR_INVALID_CAP;
1901
1902 string cap_perm;
1903 uint32_t perm = 0;
1904 if (pos < (int)cap.size() - 1) {
1905 cap_perm = cap.substr(pos + 1);
1906 int r = RGWUserCaps::parse_cap_perm(cap_perm, &perm);
1907 if (r < 0)
1908 return r;
1909 }
1910
1911 *pperm = perm;
1912
1913 return 0;
1914}
1915
1916int RGWUserCaps::add_cap(const string& cap)
1917{
1918 uint32_t perm;
1919 string type;
1920
1921 int r = get_cap(cap, type, &perm);
1922 if (r < 0)
1923 return r;
1924
1925 caps[type] |= perm;
1926
1927 return 0;
1928}
1929
1930int RGWUserCaps::remove_cap(const string& cap)
1931{
1932 uint32_t perm;
1933 string type;
1934
1935 int r = get_cap(cap, type, &perm);
1936 if (r < 0)
1937 return r;
1938
1939 map<string, uint32_t>::iterator iter = caps.find(type);
1940 if (iter == caps.end())
1941 return 0;
1942
1943 uint32_t& old_perm = iter->second;
1944 old_perm &= ~perm;
1945 if (!old_perm)
1946 caps.erase(iter);
1947
1948 return 0;
1949}
1950
1951int RGWUserCaps::add_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 = add_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
1969int RGWUserCaps::remove_from_string(const string& str)
1970{
1971 int start = 0;
1972 do {
31f18b77
FG
1973 auto end = str.find(';', start);
1974 if (end == string::npos)
7c673cae
FG
1975 end = str.size();
1976
1977 int r = remove_cap(str.substr(start, end - start));
1978 if (r < 0)
1979 return r;
1980
1981 start = end + 1;
1982 } while (start < (int)str.size());
1983
1984 return 0;
1985}
1986
1987void RGWUserCaps::dump(Formatter *f) const
1988{
1989 dump(f, "caps");
1990}
1991
1992void RGWUserCaps::dump(Formatter *f, const char *name) const
1993{
1994 f->open_array_section(name);
1995 map<string, uint32_t>::const_iterator iter;
1996 for (iter = caps.begin(); iter != caps.end(); ++iter)
1997 {
1998 f->open_object_section("cap");
1999 f->dump_string("type", iter->first);
2000 uint32_t perm = iter->second;
2001 string perm_str;
2002 for (int i=0; cap_names[i].type_name; i++) {
2003 if ((perm & cap_names[i].flag) == cap_names[i].flag) {
2004 if (perm_str.size())
2005 perm_str.append(", ");
2006
2007 perm_str.append(cap_names[i].type_name);
2008 perm &= ~cap_names[i].flag;
2009 }
2010 }
2011 if (perm_str.empty())
2012 perm_str = "<none>";
2013
2014 f->dump_string("perm", perm_str);
2015 f->close_section();
2016 }
2017
2018 f->close_section();
2019}
2020
2021struct RGWUserCap {
2022 string type;
2023 uint32_t perm;
2024
2025 void decode_json(JSONObj *obj) {
2026 JSONDecoder::decode_json("type", type, obj);
2027 string perm_str;
2028 JSONDecoder::decode_json("perm", perm_str, obj);
2029 if (RGWUserCaps::parse_cap_perm(perm_str, &perm) < 0) {
2030 throw JSONDecoder::err("failed to parse permissions");
2031 }
2032 }
2033};
2034
2035void RGWUserCaps::decode_json(JSONObj *obj)
2036{
2037 list<RGWUserCap> caps_list;
2038 decode_json_obj(caps_list, obj);
2039
2040 list<RGWUserCap>::iterator iter;
2041 for (iter = caps_list.begin(); iter != caps_list.end(); ++iter) {
2042 RGWUserCap& cap = *iter;
2043 caps[cap.type] = cap.perm;
2044 }
2045}
2046
9f95a23c 2047int RGWUserCaps::check_cap(const string& cap, uint32_t perm) const
7c673cae 2048{
9f95a23c 2049 auto iter = caps.find(cap);
7c673cae
FG
2050
2051 if ((iter == caps.end()) ||
2052 (iter->second & perm) != perm) {
2053 return -EPERM;
2054 }
2055
2056 return 0;
2057}
2058
2059bool RGWUserCaps::is_valid_cap_type(const string& tp)
2060{
2061 static const char *cap_type[] = { "user",
2062 "users",
2063 "buckets",
2064 "metadata",
20effc67 2065 "info",
7c673cae
FG
2066 "usage",
2067 "zone",
2068 "bilog",
2069 "mdlog",
2070 "datalog",
11fdf7f2 2071 "roles",
f6b5b4d7 2072 "user-policy",
f91f0fd5 2073 "amz-cache",
20effc67
TL
2074 "oidc-provider",
2075 "ratelimit"};
7c673cae
FG
2076
2077 for (unsigned int i = 0; i < sizeof(cap_type) / sizeof(char *); ++i) {
2078 if (tp.compare(cap_type[i]) == 0) {
2079 return true;
2080 }
2081 }
2082
2083 return false;
2084}
2085
7c673cae
FG
2086void rgw_pool::from_str(const string& s)
2087{
1adf2230 2088 size_t pos = rgw_unescape_str(s, 0, '\\', ':', &name);
7c673cae 2089 if (pos != string::npos) {
1adf2230 2090 pos = rgw_unescape_str(s, pos, '\\', ':', &ns);
7c673cae
FG
2091 /* ignore return; if pos != string::npos it means that we had a colon
2092 * in the middle of ns that wasn't escaped, we're going to stop there
2093 */
2094 }
2095}
2096
2097string rgw_pool::to_str() const
2098{
2099 string esc_name;
1adf2230 2100 rgw_escape_str(name, '\\', ':', &esc_name);
7c673cae
FG
2101 if (ns.empty()) {
2102 return esc_name;
2103 }
2104 string esc_ns;
1adf2230 2105 rgw_escape_str(ns, '\\', ':', &esc_ns);
7c673cae
FG
2106 return esc_name + ":" + esc_ns;
2107}
2108
11fdf7f2 2109void rgw_raw_obj::decode_from_rgw_obj(bufferlist::const_iterator& bl)
7c673cae 2110{
11fdf7f2 2111 using ceph::decode;
7c673cae 2112 rgw_obj old_obj;
11fdf7f2 2113 decode(old_obj, bl);
7c673cae
FG
2114
2115 get_obj_bucket_and_oid_loc(old_obj, oid, loc);
2116 pool = old_obj.get_explicit_data_pool();
2117}
2118
7c673cae
FG
2119static struct rgw_name_to_flag op_type_mapping[] = { {"*", RGW_OP_TYPE_ALL},
2120 {"read", RGW_OP_TYPE_READ},
2121 {"write", RGW_OP_TYPE_WRITE},
2122 {"delete", RGW_OP_TYPE_DELETE},
2123 {NULL, 0} };
2124
2125
2126int rgw_parse_op_type_list(const string& str, uint32_t *perm)
2127{
9f95a23c 2128 return rgw_parse_list_of_flags(op_type_mapping, str, perm);
7c673cae
FG
2129}
2130
f67539c2 2131bool match_policy(std::string_view pattern, std::string_view input,
d2e6a577 2132 uint32_t flag)
7c673cae 2133{
d2e6a577
FG
2134 const uint32_t flag2 = flag & (MATCH_POLICY_ACTION|MATCH_POLICY_ARN) ?
2135 MATCH_CASE_INSENSITIVE : 0;
f64942e4
AA
2136 const bool colonblocks = !(flag & (MATCH_POLICY_RESOURCE |
2137 MATCH_POLICY_STRING));
7c673cae 2138
f67539c2
TL
2139 const auto npos = std::string_view::npos;
2140 std::string_view::size_type last_pos_input = 0, last_pos_pattern = 0;
d2e6a577 2141 while (true) {
f64942e4
AA
2142 auto cur_pos_input = colonblocks ? input.find(":", last_pos_input) : npos;
2143 auto cur_pos_pattern =
2144 colonblocks ? pattern.find(":", last_pos_pattern) : npos;
7c673cae 2145
d2e6a577
FG
2146 auto substr_input = input.substr(last_pos_input, cur_pos_input);
2147 auto substr_pattern = pattern.substr(last_pos_pattern, cur_pos_pattern);
7c673cae 2148
d2e6a577
FG
2149 if (!match_wildcards(substr_pattern, substr_input, flag2))
2150 return false;
2151
2152 if (cur_pos_pattern == npos)
2153 return cur_pos_input == npos;
2154 if (cur_pos_input == npos)
2155 return false;
7c673cae
FG
2156
2157 last_pos_pattern = cur_pos_pattern + 1;
2158 last_pos_input = cur_pos_input + 1;
2159 }
2160}
11fdf7f2
TL
2161
2162/*
2163 * make attrs look-like-this
2164 * converts underscores to dashes
2165 */
2166string lowercase_dash_http_attr(const string& orig)
2167{
2168 const char *s = orig.c_str();
2169 char buf[orig.size() + 1];
2170 buf[orig.size()] = '\0';
2171
2172 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2173 switch (*s) {
2174 case '_':
2175 buf[i] = '-';
2176 break;
2177 default:
2178 buf[i] = tolower(*s);
2179 }
2180 }
2181 return string(buf);
2182}
2183
2184/*
2185 * make attrs Look-Like-This
2186 * converts underscores to dashes
2187 */
2188string camelcase_dash_http_attr(const string& orig)
2189{
2190 const char *s = orig.c_str();
2191 char buf[orig.size() + 1];
2192 buf[orig.size()] = '\0';
2193
2194 bool last_sep = true;
2195
2196 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2197 switch (*s) {
2198 case '_':
2199 case '-':
2200 buf[i] = '-';
2201 last_sep = true;
2202 break;
2203 default:
2204 if (last_sep) {
2205 buf[i] = toupper(*s);
2206 } else {
2207 buf[i] = tolower(*s);
2208 }
2209 last_sep = false;
2210 }
2211 }
2212 return string(buf);
2213}
9f95a23c
TL
2214
2215RGWBucketInfo::RGWBucketInfo()
2216{
2217}
2218
2219RGWBucketInfo::~RGWBucketInfo()
2220{
2221}
2222
2223void RGWBucketInfo::encode(bufferlist& bl) const {
f67539c2 2224 ENCODE_START(23, 4, bl);
9f95a23c
TL
2225 encode(bucket, bl);
2226 encode(owner.id, bl);
2227 encode(flags, bl);
2228 encode(zonegroup, bl);
2229 uint64_t ct = real_clock::to_time_t(creation_time);
2230 encode(ct, bl);
2231 encode(placement_rule, bl);
2232 encode(has_instance_obj, bl);
2233 encode(quota, bl);
9f95a23c
TL
2234 encode(requester_pays, bl);
2235 encode(owner.tenant, bl);
2236 encode(has_website, bl);
2237 if (has_website) {
2238 encode(website_conf, bl);
2239 }
9f95a23c
TL
2240 encode(swift_versioning, bl);
2241 if (swift_versioning) {
2242 encode(swift_ver_location, bl);
2243 }
2244 encode(creation_time, bl);
2245 encode(mdsearch_config, bl);
2246 encode(reshard_status, bl);
2247 encode(new_bucket_instance_id, bl);
2248 if (obj_lock_enabled()) {
2249 encode(obj_lock, bl);
2250 }
2251 bool has_sync_policy = !empty_sync_policy();
2252 encode(has_sync_policy, bl);
2253 if (has_sync_policy) {
2254 encode(*sync_policy, bl);
2255 }
f67539c2
TL
2256 encode(layout, bl);
2257 encode(owner.ns, bl);
9f95a23c
TL
2258 ENCODE_FINISH(bl);
2259}
2260
2261void RGWBucketInfo::decode(bufferlist::const_iterator& bl) {
f67539c2 2262 DECODE_START_LEGACY_COMPAT_LEN_32(23, 4, 4, bl);
9f95a23c
TL
2263 decode(bucket, bl);
2264 if (struct_v >= 2) {
2265 string s;
2266 decode(s, bl);
2267 owner.from_str(s);
2268 }
2269 if (struct_v >= 3)
2270 decode(flags, bl);
2271 if (struct_v >= 5)
2272 decode(zonegroup, bl);
2273 if (struct_v >= 6) {
2274 uint64_t ct;
2275 decode(ct, bl);
2276 if (struct_v < 17)
2277 creation_time = ceph::real_clock::from_time_t((time_t)ct);
2278 }
2279 if (struct_v >= 7)
2280 decode(placement_rule, bl);
2281 if (struct_v >= 8)
2282 decode(has_instance_obj, bl);
2283 if (struct_v >= 9)
2284 decode(quota, bl);
f67539c2
TL
2285 static constexpr uint8_t new_layout_v = 22;
2286 if (struct_v >= 10 && struct_v < new_layout_v)
2287 decode(layout.current_index.layout.normal.num_shards, bl);
2288 if (struct_v >= 11 && struct_v < new_layout_v)
2289 decode(layout.current_index.layout.normal.hash_type, bl);
9f95a23c
TL
2290 if (struct_v >= 12)
2291 decode(requester_pays, bl);
2292 if (struct_v >= 13)
2293 decode(owner.tenant, bl);
2294 if (struct_v >= 14) {
2295 decode(has_website, bl);
2296 if (has_website) {
2297 decode(website_conf, bl);
2298 } else {
2299 website_conf = RGWBucketWebsiteConf();
2300 }
2301 }
f67539c2 2302 if (struct_v >= 15 && struct_v < new_layout_v) {
9f95a23c
TL
2303 uint32_t it;
2304 decode(it, bl);
f67539c2 2305 layout.current_index.layout.type = (rgw::BucketIndexType)it;
9f95a23c 2306 } else {
f67539c2 2307 layout.current_index.layout.type = rgw::BucketIndexType::Normal;
9f95a23c
TL
2308 }
2309 swift_versioning = false;
2310 swift_ver_location.clear();
2311 if (struct_v >= 16) {
2312 decode(swift_versioning, bl);
2313 if (swift_versioning) {
2314 decode(swift_ver_location, bl);
2315 }
2316 }
2317 if (struct_v >= 17) {
2318 decode(creation_time, bl);
2319 }
2320 if (struct_v >= 18) {
2321 decode(mdsearch_config, bl);
2322 }
2323 if (struct_v >= 19) {
2324 decode(reshard_status, bl);
2325 decode(new_bucket_instance_id, bl);
2326 }
2327 if (struct_v >= 20 && obj_lock_enabled()) {
2328 decode(obj_lock, bl);
2329 }
2330 if (struct_v >= 21) {
2331 decode(sync_policy, bl);
2332 }
f67539c2
TL
2333 if (struct_v >= 22) {
2334 decode(layout, bl);
2335 }
2336 if (struct_v >= 23) {
2337 decode(owner.ns, bl);
2338 }
a4b75251
TL
2339
2340 if (layout.logs.empty() &&
2341 layout.current_index.layout.type == rgw::BucketIndexType::Normal) {
2342 layout.logs.push_back(rgw::log_layout_from_index(0, layout.current_index.layout.normal));
2343 }
9f95a23c
TL
2344 DECODE_FINISH(bl);
2345}
2346
2347void RGWBucketInfo::set_sync_policy(rgw_sync_policy_info&& policy)
2348{
2349 sync_policy = std::move(policy);
2350}
2351
2352bool RGWBucketInfo::empty_sync_policy() const
2353{
2354 if (!sync_policy) {
2355 return true;
2356 }
2357
2358 return sync_policy->empty();
2359}
2360
20effc67
TL
2361struct rgw_pool;
2362struct rgw_placement_rule;
2363class RGWUserCaps;
2364
2365void decode_json_obj(rgw_pool& pool, JSONObj *obj)
2366{
2367 string s;
2368 decode_json_obj(s, obj);
2369 pool = rgw_pool(s);
2370}
2371
2372void encode_json(const char *name, const rgw_placement_rule& r, Formatter *f)
2373{
2374 encode_json(name, r.to_str(), f);
2375}
2376
2377void encode_json(const char *name, const rgw_pool& pool, Formatter *f)
2378{
2379 f->dump_string(name, pool.to_str());
2380}
2381
2382void encode_json(const char *name, const RGWUserCaps& val, Formatter *f)
2383{
2384 val.dump(f, name);
2385}
2386
2387void RGWBucketEnt::generate_test_instances(list<RGWBucketEnt*>& o)
2388{
2389 RGWBucketEnt *e = new RGWBucketEnt;
2390 init_bucket(&e->bucket, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2391 e->size = 1024;
2392 e->size_rounded = 4096;
2393 e->count = 1;
2394 o.push_back(e);
2395 o.push_back(new RGWBucketEnt);
2396}
2397
2398void RGWBucketEnt::dump(Formatter *f) const
2399{
2400 encode_json("bucket", bucket, f);
2401 encode_json("size", size, f);
2402 encode_json("size_rounded", size_rounded, f);
2403 utime_t ut(creation_time);
2404 encode_json("mtime", ut, f); /* mtime / creation time discrepency needed for backward compatibility */
2405 encode_json("count", count, f);
2406 encode_json("placement_rule", placement_rule.to_str(), f);
2407}
2408
2409void rgw_obj::generate_test_instances(list<rgw_obj*>& o)
2410{
2411 rgw_bucket b;
2412 init_bucket(&b, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2413 rgw_obj *obj = new rgw_obj(b, "object");
2414 o.push_back(obj);
2415 o.push_back(new rgw_obj);
2416}
2417
2418void rgw_bucket_placement::dump(Formatter *f) const
2419{
2420 encode_json("bucket", bucket, f);
2421 encode_json("placement_rule", placement_rule, f);
2422}
2423
2424void RGWBucketInfo::generate_test_instances(list<RGWBucketInfo*>& o)
2425{
2426 // Since things without a log will have one synthesized on decode,
2427 // ensure the things we attempt to encode will have one added so we
2428 // round-trip properly.
2429 auto gen_layout = [](rgw::BucketLayout& l) {
2430 l.current_index.gen = 0;
2431 l.current_index.layout.normal.hash_type = rgw::BucketHashType::Mod;
2432 l.current_index.layout.type = rgw::BucketIndexType::Normal;
2433 l.current_index.layout.normal.num_shards = 11;
2434 l.logs.push_back(log_layout_from_index(
2435 l.current_index.gen,
2436 l.current_index.layout.normal));
2437 };
2438
2439
2440 RGWBucketInfo *i = new RGWBucketInfo;
2441 init_bucket(&i->bucket, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2442 i->owner = "owner";
2443 i->flags = BUCKET_SUSPENDED;
2444 gen_layout(i->layout);
2445 o.push_back(i);
2446 i = new RGWBucketInfo;
2447 gen_layout(i->layout);
2448 o.push_back(i);
2449}
2450
2451void RGWBucketInfo::dump(Formatter *f) const
2452{
2453 encode_json("bucket", bucket, f);
2454 utime_t ut(creation_time);
2455 encode_json("creation_time", ut, f);
2456 encode_json("owner", owner.to_str(), f);
2457 encode_json("flags", flags, f);
2458 encode_json("zonegroup", zonegroup, f);
2459 encode_json("placement_rule", placement_rule, f);
2460 encode_json("has_instance_obj", has_instance_obj, f);
2461 encode_json("quota", quota, f);
2462 encode_json("num_shards", layout.current_index.layout.normal.num_shards, f);
2463 encode_json("bi_shard_hash_type", (uint32_t)layout.current_index.layout.normal.hash_type, f);
2464 encode_json("requester_pays", requester_pays, f);
2465 encode_json("has_website", has_website, f);
2466 if (has_website) {
2467 encode_json("website_conf", website_conf, f);
2468 }
2469 encode_json("swift_versioning", swift_versioning, f);
2470 encode_json("swift_ver_location", swift_ver_location, f);
2471 encode_json("index_type", (uint32_t)layout.current_index.layout.type, f);
2472 encode_json("mdsearch_config", mdsearch_config, f);
2473 encode_json("reshard_status", (int)reshard_status, f);
2474 encode_json("new_bucket_instance_id", new_bucket_instance_id, f);
2475 if (!empty_sync_policy()) {
2476 encode_json("sync_policy", *sync_policy, f);
2477 }
2478}
2479
2480void RGWBucketInfo::decode_json(JSONObj *obj) {
2481 JSONDecoder::decode_json("bucket", bucket, obj);
2482 utime_t ut;
2483 JSONDecoder::decode_json("creation_time", ut, obj);
2484 creation_time = ut.to_real_time();
2485 JSONDecoder::decode_json("owner", owner, obj);
2486 JSONDecoder::decode_json("flags", flags, obj);
2487 JSONDecoder::decode_json("zonegroup", zonegroup, obj);
2488 /* backward compatability with region */
2489 if (zonegroup.empty()) {
2490 JSONDecoder::decode_json("region", zonegroup, obj);
2491 }
2492 string pr;
2493 JSONDecoder::decode_json("placement_rule", pr, obj);
2494 placement_rule.from_str(pr);
2495 JSONDecoder::decode_json("has_instance_obj", has_instance_obj, obj);
2496 JSONDecoder::decode_json("quota", quota, obj);
2497 JSONDecoder::decode_json("num_shards", layout.current_index.layout.normal.num_shards, obj);
2498 uint32_t hash_type;
2499 JSONDecoder::decode_json("bi_shard_hash_type", hash_type, obj);
2500 layout.current_index.layout.normal.hash_type = static_cast<rgw::BucketHashType>(hash_type);
2501 JSONDecoder::decode_json("requester_pays", requester_pays, obj);
2502 JSONDecoder::decode_json("has_website", has_website, obj);
2503 if (has_website) {
2504 JSONDecoder::decode_json("website_conf", website_conf, obj);
2505 }
2506 JSONDecoder::decode_json("swift_versioning", swift_versioning, obj);
2507 JSONDecoder::decode_json("swift_ver_location", swift_ver_location, obj);
2508 uint32_t it;
2509 JSONDecoder::decode_json("index_type", it, obj);
2510 layout.current_index.layout.type = (rgw::BucketIndexType)it;
2511 JSONDecoder::decode_json("mdsearch_config", mdsearch_config, obj);
2512 int rs;
2513 JSONDecoder::decode_json("reshard_status", rs, obj);
2514 reshard_status = (cls_rgw_reshard_status)rs;
2515
2516 rgw_sync_policy_info sp;
2517 JSONDecoder::decode_json("sync_policy", sp, obj);
2518 if (!sp.empty()) {
2519 set_sync_policy(std::move(sp));
2520 }
2521}
2522
2523void RGWUserInfo::generate_test_instances(list<RGWUserInfo*>& o)
2524{
2525 RGWUserInfo *i = new RGWUserInfo;
2526 i->user_id = "user_id";
2527 i->display_name = "display_name";
2528 i->user_email = "user@email";
2529 RGWAccessKey k1, k2;
2530 k1.id = "id1";
2531 k1.key = "key1";
2532 k2.id = "id2";
2533 k2.subuser = "subuser";
2534 RGWSubUser u;
2535 u.name = "id2";
2536 u.perm_mask = 0x1;
2537 i->access_keys[k1.id] = k1;
2538 i->swift_keys[k2.id] = k2;
2539 i->subusers[u.name] = u;
2540 o.push_back(i);
2541
2542 o.push_back(new RGWUserInfo);
2543}
2544
2545static void user_info_dump_subuser(const char *name, const RGWSubUser& subuser, Formatter *f, void *parent)
2546{
2547 RGWUserInfo *info = static_cast<RGWUserInfo *>(parent);
2548 subuser.dump(f, info->user_id.to_str());
2549}
2550
2551static void user_info_dump_key(const char *name, const RGWAccessKey& key, Formatter *f, void *parent)
2552{
2553 RGWUserInfo *info = static_cast<RGWUserInfo *>(parent);
2554 key.dump(f, info->user_id.to_str(), false);
2555}
2556
2557static void user_info_dump_swift_key(const char *name, const RGWAccessKey& key, Formatter *f, void *parent)
2558{
2559 RGWUserInfo *info = static_cast<RGWUserInfo *>(parent);
2560 key.dump(f, info->user_id.to_str(), true);
2561}
2562
2563static void decode_access_keys(map<string, RGWAccessKey>& m, JSONObj *o)
2564{
2565 RGWAccessKey k;
2566 k.decode_json(o);
2567 m[k.id] = k;
2568}
2569
2570static void decode_swift_keys(map<string, RGWAccessKey>& m, JSONObj *o)
2571{
2572 RGWAccessKey k;
2573 k.decode_json(o, true);
2574 m[k.id] = k;
2575}
2576
2577static void decode_subusers(map<string, RGWSubUser>& m, JSONObj *o)
2578{
2579 RGWSubUser u;
2580 u.decode_json(o);
2581 m[u.name] = u;
2582}
2583
2584
2585struct rgw_flags_desc {
2586 uint32_t mask;
2587 const char *str;
2588};
2589
2590static struct rgw_flags_desc rgw_perms[] = {
2591 { RGW_PERM_FULL_CONTROL, "full-control" },
2592 { RGW_PERM_READ | RGW_PERM_WRITE, "read-write" },
2593 { RGW_PERM_READ, "read" },
2594 { RGW_PERM_WRITE, "write" },
2595 { RGW_PERM_READ_ACP, "read-acp" },
2596 { RGW_PERM_WRITE_ACP, "write-acp" },
2597 { 0, NULL }
2598};
2599
2600void rgw_perm_to_str(uint32_t mask, char *buf, int len)
2601{
2602 const char *sep = "";
2603 int pos = 0;
2604 if (!mask) {
2605 snprintf(buf, len, "<none>");
2606 return;
2607 }
2608 while (mask) {
2609 uint32_t orig_mask = mask;
2610 for (int i = 0; rgw_perms[i].mask; i++) {
2611 struct rgw_flags_desc *desc = &rgw_perms[i];
2612 if ((mask & desc->mask) == desc->mask) {
2613 pos += snprintf(buf + pos, len - pos, "%s%s", sep, desc->str);
2614 if (pos == len)
2615 return;
2616 sep = ", ";
2617 mask &= ~desc->mask;
2618 if (!mask)
2619 return;
2620 }
2621 }
2622 if (mask == orig_mask) // no change
2623 break;
2624 }
2625}
2626
2627uint32_t rgw_str_to_perm(const char *str)
2628{
2629 if (strcasecmp(str, "") == 0)
2630 return RGW_PERM_NONE;
2631 else if (strcasecmp(str, "read") == 0)
2632 return RGW_PERM_READ;
2633 else if (strcasecmp(str, "write") == 0)
2634 return RGW_PERM_WRITE;
2635 else if (strcasecmp(str, "readwrite") == 0)
2636 return RGW_PERM_READ | RGW_PERM_WRITE;
2637 else if (strcasecmp(str, "full") == 0)
2638 return RGW_PERM_FULL_CONTROL;
2639
2640 return RGW_PERM_INVALID;
2641}
2642
2643template <class T>
2644static void mask_to_str(T *mask_list, uint32_t mask, char *buf, int len)
2645{
2646 const char *sep = "";
2647 int pos = 0;
2648 if (!mask) {
2649 snprintf(buf, len, "<none>");
2650 return;
2651 }
2652 while (mask) {
2653 uint32_t orig_mask = mask;
2654 for (int i = 0; mask_list[i].mask; i++) {
2655 T *desc = &mask_list[i];
2656 if ((mask & desc->mask) == desc->mask) {
2657 pos += snprintf(buf + pos, len - pos, "%s%s", sep, desc->str);
2658 if (pos == len)
2659 return;
2660 sep = ", ";
2661 mask &= ~desc->mask;
2662 if (!mask)
2663 return;
2664 }
2665 }
2666 if (mask == orig_mask) // no change
2667 break;
2668 }
2669}
2670
2671static void perm_to_str(uint32_t mask, char *buf, int len)
2672{
2673 return mask_to_str(rgw_perms, mask, buf, len);
2674}
2675
2676static struct rgw_flags_desc op_type_flags[] = {
2677 { RGW_OP_TYPE_READ, "read" },
2678 { RGW_OP_TYPE_WRITE, "write" },
2679 { RGW_OP_TYPE_DELETE, "delete" },
2680 { 0, NULL }
2681};
2682
2683void op_type_to_str(uint32_t mask, char *buf, int len)
2684{
2685 return mask_to_str(op_type_flags, mask, buf, len);
2686}
2687
2688void RGWRateLimitInfo::decode_json(JSONObj *obj)
2689{
2690 JSONDecoder::decode_json("max_read_ops", max_read_ops, obj);
2691 JSONDecoder::decode_json("max_write_ops", max_write_ops, obj);
2692 JSONDecoder::decode_json("max_read_bytes", max_read_ops, obj);
2693 JSONDecoder::decode_json("max_write_bytes", max_write_ops, obj);
2694 JSONDecoder::decode_json("enabled", enabled, obj);
2695}
2696
2697void RGWRateLimitInfo::dump(Formatter *f) const
2698{
2699 f->dump_int("max_read_ops", max_read_ops);
2700 f->dump_int("max_write_ops", max_write_ops);
2701 f->dump_int("max_read_bytes", max_read_bytes);
2702 f->dump_int("max_write_bytes", max_write_bytes);
2703 f->dump_bool("enabled", enabled);
2704}
2705
2706void RGWUserInfo::dump(Formatter *f) const
2707{
2708
2709 encode_json("user_id", user_id.to_str(), f);
2710 encode_json("display_name", display_name, f);
2711 encode_json("email", user_email, f);
2712 encode_json("suspended", (int)suspended, f);
2713 encode_json("max_buckets", (int)max_buckets, f);
2714
2715 encode_json_map("subusers", NULL, "subuser", NULL, user_info_dump_subuser,(void *)this, subusers, f);
2716 encode_json_map("keys", NULL, "key", NULL, user_info_dump_key,(void *)this, access_keys, f);
2717 encode_json_map("swift_keys", NULL, "key", NULL, user_info_dump_swift_key,(void *)this, swift_keys, f);
2718
2719 encode_json("caps", caps, f);
2720
2721 char buf[256];
2722 op_type_to_str(op_mask, buf, sizeof(buf));
2723 encode_json("op_mask", (const char *)buf, f);
2724
2725 if (system) { /* no need to show it for every user */
2726 encode_json("system", (bool)system, f);
2727 }
2728 if (admin) {
2729 encode_json("admin", (bool)admin, f);
2730 }
2731 encode_json("default_placement", default_placement.name, f);
2732 encode_json("default_storage_class", default_placement.storage_class, f);
2733 encode_json("placement_tags", placement_tags, f);
2734 encode_json("bucket_quota", bucket_quota, f);
2735 encode_json("user_quota", user_quota, f);
2736 encode_json("temp_url_keys", temp_url_keys, f);
2737
2738 string user_source_type;
2739 switch ((RGWIdentityType)type) {
2740 case TYPE_RGW:
2741 user_source_type = "rgw";
2742 break;
2743 case TYPE_KEYSTONE:
2744 user_source_type = "keystone";
2745 break;
2746 case TYPE_LDAP:
2747 user_source_type = "ldap";
2748 break;
2749 case TYPE_NONE:
2750 user_source_type = "none";
2751 break;
2752 default:
2753 user_source_type = "none";
2754 break;
2755 }
2756 encode_json("type", user_source_type, f);
2757 encode_json("mfa_ids", mfa_ids, f);
2758}
2759
2760void RGWUserInfo::decode_json(JSONObj *obj)
2761{
2762 string uid;
2763
2764 JSONDecoder::decode_json("user_id", uid, obj, true);
2765 user_id.from_str(uid);
2766
2767 JSONDecoder::decode_json("display_name", display_name, obj);
2768 JSONDecoder::decode_json("email", user_email, obj);
2769 bool susp = false;
2770 JSONDecoder::decode_json("suspended", susp, obj);
2771 suspended = (__u8)susp;
2772 JSONDecoder::decode_json("max_buckets", max_buckets, obj);
2773
2774 JSONDecoder::decode_json("keys", access_keys, decode_access_keys, obj);
2775 JSONDecoder::decode_json("swift_keys", swift_keys, decode_swift_keys, obj);
2776 JSONDecoder::decode_json("subusers", subusers, decode_subusers, obj);
2777
2778 JSONDecoder::decode_json("caps", caps, obj);
2779
2780 string mask_str;
2781 JSONDecoder::decode_json("op_mask", mask_str, obj);
2782 rgw_parse_op_type_list(mask_str, &op_mask);
2783
2784 bool sys = false;
2785 JSONDecoder::decode_json("system", sys, obj);
2786 system = (__u8)sys;
2787 bool ad = false;
2788 JSONDecoder::decode_json("admin", ad, obj);
2789 admin = (__u8)ad;
2790 JSONDecoder::decode_json("default_placement", default_placement.name, obj);
2791 JSONDecoder::decode_json("default_storage_class", default_placement.storage_class, obj);
2792 JSONDecoder::decode_json("placement_tags", placement_tags, obj);
2793 JSONDecoder::decode_json("bucket_quota", bucket_quota, obj);
2794 JSONDecoder::decode_json("user_quota", user_quota, obj);
2795 JSONDecoder::decode_json("temp_url_keys", temp_url_keys, obj);
2796
2797 string user_source_type;
2798 JSONDecoder::decode_json("type", user_source_type, obj);
2799 if (user_source_type == "rgw") {
2800 type = TYPE_RGW;
2801 } else if (user_source_type == "keystone") {
2802 type = TYPE_KEYSTONE;
2803 } else if (user_source_type == "ldap") {
2804 type = TYPE_LDAP;
2805 } else if (user_source_type == "none") {
2806 type = TYPE_NONE;
2807 }
2808 JSONDecoder::decode_json("mfa_ids", mfa_ids, obj);
2809}
2810
2811
2812void RGWSubUser::generate_test_instances(list<RGWSubUser*>& o)
2813{
2814 RGWSubUser *u = new RGWSubUser;
2815 u->name = "name";
2816 u->perm_mask = 0xf;
2817 o.push_back(u);
2818 o.push_back(new RGWSubUser);
2819}
2820
2821void RGWSubUser::dump(Formatter *f) const
2822{
2823 encode_json("id", name, f);
2824 char buf[256];
2825 perm_to_str(perm_mask, buf, sizeof(buf));
2826 encode_json("permissions", (const char *)buf, f);
2827}
2828
2829void RGWSubUser::dump(Formatter *f, const string& user) const
2830{
2831 string s = user;
2832 s.append(":");
2833 s.append(name);
2834 encode_json("id", s, f);
2835 char buf[256];
2836 perm_to_str(perm_mask, buf, sizeof(buf));
2837 encode_json("permissions", (const char *)buf, f);
2838}
2839
2840uint32_t str_to_perm(const string& s)
2841{
2842 if (s.compare("read") == 0)
2843 return RGW_PERM_READ;
2844 else if (s.compare("write") == 0)
2845 return RGW_PERM_WRITE;
2846 else if (s.compare("read-write") == 0)
2847 return RGW_PERM_READ | RGW_PERM_WRITE;
2848 else if (s.compare("full-control") == 0)
2849 return RGW_PERM_FULL_CONTROL;
2850 return 0;
2851}
2852
2853void RGWSubUser::decode_json(JSONObj *obj)
2854{
2855 string uid;
2856 JSONDecoder::decode_json("id", uid, obj);
2857 int pos = uid.find(':');
2858 if (pos >= 0)
2859 name = uid.substr(pos + 1);
2860 string perm_str;
2861 JSONDecoder::decode_json("permissions", perm_str, obj);
2862 perm_mask = str_to_perm(perm_str);
2863}
2864
2865void RGWAccessKey::generate_test_instances(list<RGWAccessKey*>& o)
2866{
2867 RGWAccessKey *k = new RGWAccessKey;
2868 k->id = "id";
2869 k->key = "key";
2870 k->subuser = "subuser";
2871 o.push_back(k);
2872 o.push_back(new RGWAccessKey);
2873}
2874
2875void RGWAccessKey::dump(Formatter *f) const
2876{
2877 encode_json("access_key", id, f);
2878 encode_json("secret_key", key, f);
2879 encode_json("subuser", subuser, f);
2880}
2881
2882void RGWAccessKey::dump_plain(Formatter *f) const
2883{
2884 encode_json("access_key", id, f);
2885 encode_json("secret_key", key, f);
2886}
2887
2888void RGWAccessKey::dump(Formatter *f, const string& user, bool swift) const
2889{
2890 string u = user;
2891 if (!subuser.empty()) {
2892 u.append(":");
2893 u.append(subuser);
2894 }
2895 encode_json("user", u, f);
2896 if (!swift) {
2897 encode_json("access_key", id, f);
2898 }
2899 encode_json("secret_key", key, f);
2900}
2901
2902void RGWAccessKey::decode_json(JSONObj *obj) {
2903 JSONDecoder::decode_json("access_key", id, obj, true);
2904 JSONDecoder::decode_json("secret_key", key, obj, true);
2905 if (!JSONDecoder::decode_json("subuser", subuser, obj)) {
2906 string user;
2907 JSONDecoder::decode_json("user", user, obj);
2908 int pos = user.find(':');
2909 if (pos >= 0) {
2910 subuser = user.substr(pos + 1);
2911 }
2912 }
2913}
2914
2915void RGWAccessKey::decode_json(JSONObj *obj, bool swift) {
2916 if (!swift) {
2917 decode_json(obj);
2918 return;
2919 }
2920
2921 if (!JSONDecoder::decode_json("subuser", subuser, obj)) {
2922 JSONDecoder::decode_json("user", id, obj, true);
2923 int pos = id.find(':');
2924 if (pos >= 0) {
2925 subuser = id.substr(pos + 1);
2926 }
2927 }
2928 JSONDecoder::decode_json("secret_key", key, obj, true);
2929}
2930
2931void RGWStorageStats::dump(Formatter *f) const
2932{
2933 encode_json("size", size, f);
2934 encode_json("size_actual", size_rounded, f);
2935 if (dump_utilized) {
2936 encode_json("size_utilized", size_utilized, f);
2937 }
2938 encode_json("size_kb", rgw_rounded_kb(size), f);
2939 encode_json("size_kb_actual", rgw_rounded_kb(size_rounded), f);
2940 if (dump_utilized) {
2941 encode_json("size_kb_utilized", rgw_rounded_kb(size_utilized), f);
2942 }
2943 encode_json("num_objects", num_objects, f);
2944}
2945
2946void rgw_obj_key::dump(Formatter *f) const
2947{
2948 encode_json("name", name, f);
2949 encode_json("instance", instance, f);
2950 encode_json("ns", ns, f);
2951}
2952
2953void rgw_obj_key::decode_json(JSONObj *obj)
2954{
2955 JSONDecoder::decode_json("name", name, obj);
2956 JSONDecoder::decode_json("instance", instance, obj);
2957 JSONDecoder::decode_json("ns", ns, obj);
2958}
2959
2960void rgw_raw_obj::dump(Formatter *f) const
2961{
2962 encode_json("pool", pool, f);
2963 encode_json("oid", oid, f);
2964 encode_json("loc", loc, f);
2965}
2966
2967void rgw_raw_obj::decode_json(JSONObj *obj) {
2968 JSONDecoder::decode_json("pool", pool, obj);
2969 JSONDecoder::decode_json("oid", oid, obj);
2970 JSONDecoder::decode_json("loc", loc, obj);
2971}
2972
2973void rgw_obj::dump(Formatter *f) const
2974{
2975 encode_json("bucket", bucket, f);
2976 encode_json("key", key, f);
2977}
2978
2979