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