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