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