]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_common.cc
buildsys: switch source download to quincy
[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
b3b6e05e 390void req_info::init_meta_info(const DoutPrefixProvider *dpp, bool *found_bad_meta)
7c673cae
FG
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) {
b3b6e05e 402 ldpp_dout(dpp, 10) << "meta>> " << p << dendl;
7c673cae
FG
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 433 for (const auto& kv: x_meta_map) {
b3b6e05e 434 ldpp_dout(dpp, 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
b3b6e05e 791int RGWHTTPArgs::parse(const DoutPrefixProvider *dpp)
7c673cae
FG
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();
b3b6e05e 823 ldpp_dout(dpp, 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,
522d829b
TL
1051 const ARN& arn,
1052 boost::optional<rgw::IAM::PolicyPrincipal&> princ_type=boost::none) {
11fdf7f2
TL
1053 if (!policy)
1054 return Effect::Pass;
1055 else
522d829b 1056 return policy->eval(env, id, op, arn, princ_type);
11fdf7f2
TL
1057}
1058
1059}
1060
522d829b 1061Effect eval_identity_or_session_policies(const vector<Policy>& policies,
11fdf7f2
TL
1062 const rgw::IAM::Environment& env,
1063 boost::optional<const rgw::auth::Identity&> id,
1064 const uint64_t op,
1065 const ARN& arn) {
522d829b
TL
1066 auto policy_res = Effect::Pass, prev_res = Effect::Pass;
1067 for (auto& policy : policies) {
1068 if (policy_res = eval_or_pass(policy, env, id, op, arn); policy_res == Effect::Deny)
1069 return policy_res;
1070 else if (policy_res == Effect::Allow)
11fdf7f2 1071 prev_res = Effect::Allow;
522d829b
TL
1072 else if (policy_res == Effect::Pass && prev_res == Effect::Allow)
1073 policy_res = Effect::Allow;
11fdf7f2 1074 }
522d829b 1075 return policy_res;
11fdf7f2
TL
1076}
1077
1078bool verify_user_permission(const DoutPrefixProvider* dpp,
9f95a23c 1079 perm_state_base * const s,
11fdf7f2
TL
1080 RGWAccessControlPolicy * const user_acl,
1081 const vector<rgw::IAM::Policy>& user_policies,
eafe8130 1082 const rgw::ARN& res,
11fdf7f2
TL
1083 const uint64_t op)
1084{
522d829b 1085 auto usr_policy_res = eval_identity_or_session_policies(user_policies, s->env, boost::none, op, res);
11fdf7f2
TL
1086 if (usr_policy_res == Effect::Deny) {
1087 return false;
1088 }
1089
1090 if (usr_policy_res == Effect::Allow) {
1091 return true;
1092 }
1093
1094 if (op == rgw::IAM::s3CreateBucket || op == rgw::IAM::s3ListAllMyBuckets) {
1095 auto perm = op_to_perm(op);
1096
1097 return verify_user_permission_no_policy(dpp, s, user_acl, perm);
1098 }
1099
1100 return false;
1101}
1102
9f95a23c
TL
1103bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1104 struct perm_state_base * const s,
1105 RGWAccessControlPolicy * const user_acl,
1106 const int perm)
7c673cae 1107{
9f95a23c 1108 if (s->identity->get_identity_type() == TYPE_ROLE)
11fdf7f2
TL
1109 return false;
1110
7c673cae
FG
1111 /* S3 doesn't support account ACLs. */
1112 if (!user_acl)
1113 return true;
1114
1115 if ((perm & (int)s->perm_mask) != perm)
1116 return false;
1117
9f95a23c 1118 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
7c673cae
FG
1119}
1120
11fdf7f2
TL
1121bool verify_user_permission(const DoutPrefixProvider* dpp,
1122 struct req_state * const s,
eafe8130 1123 const rgw::ARN& res,
11fdf7f2
TL
1124 const uint64_t op)
1125{
9f95a23c
TL
1126 perm_state_from_req_state ps(s);
1127 return verify_user_permission(dpp, &ps, s->user_acl.get(), s->iam_user_policies, res, op);
11fdf7f2
TL
1128}
1129
1130bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
1131 struct req_state * const s,
1132 const int perm)
7c673cae 1133{
9f95a23c
TL
1134 perm_state_from_req_state ps(s);
1135 return verify_user_permission_no_policy(dpp, &ps, s->user_acl.get(), perm);
7c673cae
FG
1136}
1137
9f95a23c 1138bool verify_requester_payer_permission(struct perm_state_base *s)
7c673cae
FG
1139{
1140 if (!s->bucket_info.requester_pays)
1141 return true;
1142
9f95a23c 1143 if (s->identity->is_owner_of(s->bucket_info.owner))
7c673cae
FG
1144 return true;
1145
9f95a23c 1146 if (s->identity->is_anonymous()) {
7c673cae
FG
1147 return false;
1148 }
1149
9f95a23c
TL
1150 auto request_payer = s->get_request_payer();
1151 if (request_payer) {
1152 return *request_payer;
7c673cae
FG
1153 }
1154
1155 return false;
1156}
1157
11fdf7f2 1158bool verify_bucket_permission(const DoutPrefixProvider* dpp,
9f95a23c 1159 struct perm_state_base * const s,
31f18b77 1160 const rgw_bucket& bucket,
7c673cae
FG
1161 RGWAccessControlPolicy * const user_acl,
1162 RGWAccessControlPolicy * const bucket_acl,
11fdf7f2 1163 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1164 const vector<Policy>& identity_policies,
1165 const vector<Policy>& session_policies,
31f18b77 1166 const uint64_t op)
7c673cae 1167{
31f18b77 1168 if (!verify_requester_payer_permission(s))
7c673cae
FG
1169 return false;
1170
522d829b
TL
1171 auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, boost::none, op, ARN(bucket));
1172 if (identity_policy_res == Effect::Deny)
11fdf7f2
TL
1173 return false;
1174
522d829b 1175 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
9f95a23c 1176 auto r = eval_or_pass(bucket_policy, s->env, *s->identity,
522d829b
TL
1177 op, ARN(bucket), princ_type);
1178 if (r == Effect::Deny)
1179 return false;
1180
1181 //Take into account session policies, if the identity making a request is a role
1182 if (!session_policies.empty()) {
1183 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, boost::none, op, ARN(bucket));
1184 if (session_policy_res == Effect::Deny) {
1185 return false;
1186 }
1187 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1188 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1189 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1190 (session_policy_res == Effect::Allow && r == Effect::Allow))
1191 return true;
1192 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1193 //Intersection of session policy and identity policy plus bucket policy
1194 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow)
1195 return true;
1196 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1197 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1198 return true;
1199 }
1200 return false;
1201 }
1202
1203 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
b32b8144
FG
1204 // It looks like S3 ACLs only GRANT permissions rather than
1205 // denying them, so this should be safe.
1206 return true;
31f18b77
FG
1207
1208 const auto perm = op_to_perm(op);
1209
11fdf7f2 1210 return verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm);
31f18b77
FG
1211}
1212
9f95a23c
TL
1213bool verify_bucket_permission(const DoutPrefixProvider* dpp,
1214 struct req_state * const s,
1215 const rgw_bucket& bucket,
1216 RGWAccessControlPolicy * const user_acl,
1217 RGWAccessControlPolicy * const bucket_acl,
1218 const boost::optional<Policy>& bucket_policy,
1219 const vector<Policy>& user_policies,
522d829b 1220 const vector<Policy>& session_policies,
9f95a23c
TL
1221 const uint64_t op)
1222{
1223 perm_state_from_req_state ps(s);
1224 return verify_bucket_permission(dpp, &ps, bucket,
1225 user_acl, bucket_acl,
1226 bucket_policy, user_policies,
522d829b 1227 session_policies, op);
9f95a23c
TL
1228}
1229
1230bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
31f18b77
FG
1231 RGWAccessControlPolicy * const user_acl,
1232 RGWAccessControlPolicy * const bucket_acl,
1233 const int perm)
1234{
1235 if (!bucket_acl)
7c673cae
FG
1236 return false;
1237
31f18b77 1238 if ((perm & (int)s->perm_mask) != perm)
7c673cae
FG
1239 return false;
1240
9f95a23c
TL
1241 if (bucket_acl->verify_permission(dpp, *s->identity, perm, perm,
1242 s->get_referer(),
1243 s->bucket_access_conf &&
1244 s->bucket_access_conf->ignore_public_acls()))
7c673cae
FG
1245 return true;
1246
1247 if (!user_acl)
1248 return false;
1249
9f95a23c
TL
1250 return user_acl->verify_permission(dpp, *s->identity, perm, perm);
1251}
1252
1253bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s,
1254 RGWAccessControlPolicy * const user_acl,
1255 RGWAccessControlPolicy * const bucket_acl,
1256 const int perm)
1257{
1258 perm_state_from_req_state ps(s);
1259 return verify_bucket_permission_no_policy(dpp,
1260 &ps,
1261 user_acl,
1262 bucket_acl,
1263 perm);
7c673cae
FG
1264}
1265
11fdf7f2 1266bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state * const s, const int perm)
31f18b77 1267{
9f95a23c
TL
1268 perm_state_from_req_state ps(s);
1269
1270 if (!verify_requester_payer_permission(&ps))
31f18b77
FG
1271 return false;
1272
11fdf7f2 1273 return verify_bucket_permission_no_policy(dpp,
9f95a23c 1274 &ps,
c07f9fc5
FG
1275 s->user_acl.get(),
1276 s->bucket_acl.get(),
1277 perm);
31f18b77
FG
1278}
1279
11fdf7f2 1280bool verify_bucket_permission(const DoutPrefixProvider* dpp, struct req_state * const s, const uint64_t op)
7c673cae 1281{
9f95a23c
TL
1282 perm_state_from_req_state ps(s);
1283
11fdf7f2 1284 return verify_bucket_permission(dpp,
9f95a23c 1285 &ps,
f67539c2 1286 s->bucket->get_key(),
7c673cae 1287 s->user_acl.get(),
c07f9fc5
FG
1288 s->bucket_acl.get(),
1289 s->iam_policy,
11fdf7f2 1290 s->iam_user_policies,
522d829b 1291 s->session_policies,
31f18b77
FG
1292 op);
1293}
1294
522d829b 1295// Authorize anyone permitted by the bucket policy, identity policies, session policies and the bucket owner
b32b8144
FG
1296// unless explicitly denied by the policy.
1297
1298int verify_bucket_owner_or_policy(struct req_state* const s,
1299 const uint64_t op)
1300{
522d829b
TL
1301 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, op, ARN(s->bucket->get_key()));
1302 if (identity_policy_res == Effect::Deny) {
f91f0fd5
TL
1303 return -EACCES;
1304 }
1305
522d829b 1306 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
b32b8144
FG
1307 auto e = eval_or_pass(s->iam_policy,
1308 s->env, *s->auth.identity,
522d829b 1309 op, ARN(s->bucket->get_key()), princ_type);
f91f0fd5
TL
1310 if (e == Effect::Deny) {
1311 return -EACCES;
1312 }
1313
522d829b
TL
1314 if (!s->session_policies.empty()) {
1315 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, boost::none, op, ARN(s->bucket->get_key()));
1316 if (session_policy_res == Effect::Deny) {
1317 return -EACCES;
1318 }
1319 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1320 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1321 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1322 (session_policy_res == Effect::Allow && e == Effect::Allow))
1323 return 0;
1324 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1325 //Intersection of session policy and identity policy plus bucket policy
1326 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow)
1327 return 0;
1328 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1329 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1330 return 0;
1331 }
1332 return -EACCES;
1333 }
1334
b32b8144 1335 if (e == Effect::Allow ||
522d829b 1336 identity_policy_res == Effect::Allow ||
b32b8144 1337 (e == Effect::Pass &&
522d829b 1338 identity_policy_res == Effect::Pass &&
b32b8144
FG
1339 s->auth.identity->is_owner_of(s->bucket_owner.get_id()))) {
1340 return 0;
1341 } else {
1342 return -EACCES;
1343 }
1344}
1345
1346
11fdf7f2 1347static inline bool check_deferred_bucket_perms(const DoutPrefixProvider* dpp,
9f95a23c 1348 struct perm_state_base * const s,
31f18b77
FG
1349 const rgw_bucket& bucket,
1350 RGWAccessControlPolicy * const user_acl,
1351 RGWAccessControlPolicy * const bucket_acl,
11fdf7f2 1352 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1353 const vector<Policy>& identity_policies,
1354 const vector<Policy>& session_policies,
31f18b77
FG
1355 const uint8_t deferred_check,
1356 const uint64_t op)
1357{
1358 return (s->defer_to_bucket_acls == deferred_check \
522d829b 1359 && verify_bucket_permission(dpp, s, bucket, user_acl, bucket_acl, bucket_policy, identity_policies, session_policies,op));
7c673cae
FG
1360}
1361
11fdf7f2 1362static inline bool check_deferred_bucket_only_acl(const DoutPrefixProvider* dpp,
9f95a23c 1363 struct perm_state_base * const s,
31f18b77
FG
1364 RGWAccessControlPolicy * const user_acl,
1365 RGWAccessControlPolicy * const bucket_acl,
1366 const uint8_t deferred_check,
1367 const int perm)
7c673cae
FG
1368{
1369 return (s->defer_to_bucket_acls == deferred_check \
11fdf7f2 1370 && verify_bucket_permission_no_policy(dpp, s, user_acl, bucket_acl, perm));
7c673cae
FG
1371}
1372
9f95a23c 1373bool verify_object_permission(const DoutPrefixProvider* dpp, struct perm_state_base * const s,
31f18b77 1374 const rgw_obj& obj,
7c673cae
FG
1375 RGWAccessControlPolicy * const user_acl,
1376 RGWAccessControlPolicy * const bucket_acl,
1377 RGWAccessControlPolicy * const object_acl,
11fdf7f2 1378 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1379 const vector<Policy>& identity_policies,
1380 const vector<Policy>& session_policies,
31f18b77 1381 const uint64_t op)
7c673cae
FG
1382{
1383 if (!verify_requester_payer_permission(s))
1384 return false;
1385
522d829b
TL
1386 auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, boost::none, op, ARN(obj));
1387 if (identity_policy_res == Effect::Deny)
11fdf7f2 1388 return false;
b32b8144 1389
522d829b
TL
1390 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
1391 auto r = eval_or_pass(bucket_policy, s->env, *s->identity, op, ARN(obj), princ_type);
1392 if (r == Effect::Deny)
1393 return false;
1394
1395 if (!session_policies.empty()) {
1396 auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, boost::none, op, ARN(obj));
1397 if (session_policy_res == Effect::Deny) {
1398 return false;
1399 }
1400 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
1401 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1402 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
1403 (session_policy_res == Effect::Allow && r == Effect::Allow))
1404 return true;
1405 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
1406 //Intersection of session policy and identity policy plus bucket policy
1407 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow)
1408 return true;
1409 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
1410 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
1411 return true;
1412 }
1413 return false;
1414 }
1415
1416 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
b32b8144
FG
1417 // It looks like S3 ACLs only GRANT permissions rather than
1418 // denying them, so this should be safe.
1419 return true;
31f18b77
FG
1420
1421 const auto perm = op_to_perm(op);
1422
11fdf7f2 1423 if (check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
522d829b 1424 identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
11fdf7f2 1425 check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy,
522d829b 1426 identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
31f18b77
FG
1427 return true;
1428 }
1429
1430 if (!object_acl) {
1431 return false;
1432 }
1433
9f95a23c
TL
1434 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1435 nullptr, /* http_referrer */
1436 s->bucket_access_conf &&
1437 s->bucket_access_conf->ignore_public_acls());
31f18b77
FG
1438 if (ret) {
1439 return true;
1440 }
1441
1442 if (!s->cct->_conf->rgw_enforce_swift_acls)
1443 return ret;
1444
1445 if ((perm & (int)s->perm_mask) != perm)
1446 return false;
1447
1448 int swift_perm = 0;
1449 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1450 swift_perm |= RGW_PERM_READ_OBJS;
1451 if (perm & RGW_PERM_WRITE)
1452 swift_perm |= RGW_PERM_WRITE_OBJS;
1453
1454 if (!swift_perm)
1455 return false;
1456
1457 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1458 otherwise the mask might not cover the swift permissions bits */
9f95a23c
TL
1459 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1460 s->get_referer()))
31f18b77
FG
1461 return true;
1462
1463 if (!user_acl)
1464 return false;
1465
9f95a23c
TL
1466 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
1467}
1468
1469bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state * const s,
1470 const rgw_obj& obj,
1471 RGWAccessControlPolicy * const user_acl,
1472 RGWAccessControlPolicy * const bucket_acl,
1473 RGWAccessControlPolicy * const object_acl,
1474 const boost::optional<Policy>& bucket_policy,
522d829b
TL
1475 const vector<Policy>& identity_policies,
1476 const vector<Policy>& session_policies,
9f95a23c
TL
1477 const uint64_t op)
1478{
1479 perm_state_from_req_state ps(s);
1480 return verify_object_permission(dpp, &ps, obj,
1481 user_acl, bucket_acl,
1482 object_acl, bucket_policy,
522d829b 1483 identity_policies, session_policies, op);
31f18b77
FG
1484}
1485
11fdf7f2 1486bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp,
9f95a23c 1487 struct perm_state_base * const s,
31f18b77
FG
1488 RGWAccessControlPolicy * const user_acl,
1489 RGWAccessControlPolicy * const bucket_acl,
1490 RGWAccessControlPolicy * const object_acl,
1491 const int perm)
1492{
11fdf7f2
TL
1493 if (check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, perm) ||
1494 check_deferred_bucket_only_acl(dpp, s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, RGW_PERM_FULL_CONTROL)) {
7c673cae
FG
1495 return true;
1496 }
1497
1498 if (!object_acl) {
1499 return false;
1500 }
1501
9f95a23c
TL
1502 bool ret = object_acl->verify_permission(dpp, *s->identity, s->perm_mask, perm,
1503 nullptr, /* http referrer */
1504 s->bucket_access_conf &&
1505 s->bucket_access_conf->ignore_public_acls());
7c673cae
FG
1506 if (ret) {
1507 return true;
1508 }
1509
1510 if (!s->cct->_conf->rgw_enforce_swift_acls)
1511 return ret;
1512
1513 if ((perm & (int)s->perm_mask) != perm)
1514 return false;
1515
1516 int swift_perm = 0;
1517 if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP))
1518 swift_perm |= RGW_PERM_READ_OBJS;
1519 if (perm & RGW_PERM_WRITE)
1520 swift_perm |= RGW_PERM_WRITE_OBJS;
1521
1522 if (!swift_perm)
1523 return false;
1524
1525 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1526 otherwise the mask might not cover the swift permissions bits */
9f95a23c
TL
1527 if (bucket_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm,
1528 s->get_referer()))
7c673cae
FG
1529 return true;
1530
1531 if (!user_acl)
1532 return false;
1533
9f95a23c 1534 return user_acl->verify_permission(dpp, *s->identity, swift_perm, swift_perm);
7c673cae
FG
1535}
1536
11fdf7f2 1537bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state *s, int perm)
31f18b77 1538{
9f95a23c
TL
1539 perm_state_from_req_state ps(s);
1540
1541 if (!verify_requester_payer_permission(&ps))
31f18b77
FG
1542 return false;
1543
11fdf7f2 1544 return verify_object_permission_no_policy(dpp,
9f95a23c 1545 &ps,
c07f9fc5
FG
1546 s->user_acl.get(),
1547 s->bucket_acl.get(),
1548 s->object_acl.get(),
1549 perm);
31f18b77
FG
1550}
1551
11fdf7f2 1552bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s, uint64_t op)
7c673cae 1553{
9f95a23c
TL
1554 perm_state_from_req_state ps(s);
1555
11fdf7f2 1556 return verify_object_permission(dpp,
9f95a23c 1557 &ps,
f67539c2 1558 rgw_obj(s->bucket->get_key(), s->object->get_key()),
c07f9fc5
FG
1559 s->user_acl.get(),
1560 s->bucket_acl.get(),
1561 s->object_acl.get(),
1562 s->iam_policy,
11fdf7f2 1563 s->iam_user_policies,
522d829b 1564 s->session_policies,
31f18b77 1565 op);
7c673cae
FG
1566}
1567
f67539c2
TL
1568
1569int verify_object_lock(const DoutPrefixProvider* dpp, const rgw::sal::RGWAttrs& attrs, const bool bypass_perm, const bool bypass_governance_mode) {
1570 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
1571 if (aiter != attrs.end()) {
1572 RGWObjectRetention obj_retention;
1573 try {
1574 decode(obj_retention, aiter->second);
1575 } catch (buffer::error& err) {
1576 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
1577 return -EIO;
1578 }
1579 if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) > ceph_clock_now()) {
1580 if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
1581 return -EACCES;
1582 }
1583 }
1584 }
1585 aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD);
1586 if (aiter != attrs.end()) {
1587 RGWObjectLegalHold obj_legal_hold;
1588 try {
1589 decode(obj_legal_hold, aiter->second);
1590 } catch (buffer::error& err) {
1591 ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl;
1592 return -EIO;
1593 }
1594 if (obj_legal_hold.is_enabled()) {
1595 return -EACCES;
1596 }
1597 }
1598
1599 return 0;
1600}
1601
1602
7c673cae
FG
1603class HexTable
1604{
1605 char table[256];
1606
1607public:
1608 HexTable() {
92f5a8d4 1609 // FIPS zeroization audit 20191115: this memset is not security related.
7c673cae
FG
1610 memset(table, -1, sizeof(table));
1611 int i;
1612 for (i = '0'; i<='9'; i++)
1613 table[i] = i - '0';
1614 for (i = 'A'; i<='F'; i++)
1615 table[i] = i - 'A' + 0xa;
1616 for (i = 'a'; i<='f'; i++)
1617 table[i] = i - 'a' + 0xa;
1618 }
1619
1620 char to_num(char c) {
1621 return table[(int)c];
1622 }
1623};
1624
1625static char hex_to_num(char c)
1626{
1627 static HexTable hex_table;
1628 return hex_table.to_num(c);
1629}
1630
f67539c2 1631std::string url_decode(const std::string_view& src_str, bool in_query)
7c673cae 1632{
31f18b77
FG
1633 std::string dest_str;
1634 dest_str.reserve(src_str.length() + 1);
7c673cae 1635
31f18b77 1636 for (auto src = std::begin(src_str); src != std::end(src_str); ++src) {
7c673cae
FG
1637 if (*src != '%') {
1638 if (!in_query || *src != '+') {
31f18b77
FG
1639 if (*src == '?') {
1640 in_query = true;
1641 }
1642 dest_str.push_back(*src);
7c673cae 1643 } else {
31f18b77 1644 dest_str.push_back(' ');
7c673cae
FG
1645 }
1646 } else {
31f18b77
FG
1647 /* 3 == strlen("%%XX") */
1648 if (std::distance(src, std::end(src_str)) < 3) {
7c673cae 1649 break;
31f18b77
FG
1650 }
1651
1652 src++;
1653 const char c1 = hex_to_num(*src++);
1654 const char c2 = hex_to_num(*src);
1655 if (c1 < 0 || c2 < 0) {
1656 return std::string();
1657 } else {
1658 dest_str.push_back(c1 << 4 | c2);
1659 }
7c673cae
FG
1660 }
1661 }
7c673cae 1662
31f18b77 1663 return dest_str;
7c673cae
FG
1664}
1665
1666void rgw_uri_escape_char(char c, string& dst)
1667{
1668 char buf[16];
1669 snprintf(buf, sizeof(buf), "%%%.2X", (int)(unsigned char)c);
1670 dst.append(buf);
1671}
1672
1673static bool char_needs_url_encoding(char c)
1674{
1675 if (c <= 0x20 || c >= 0x7f)
1676 return true;
1677
1678 switch (c) {
1679 case 0x22:
1680 case 0x23:
1681 case 0x25:
1682 case 0x26:
1683 case 0x2B:
1684 case 0x2C:
1685 case 0x2F:
1686 case 0x3A:
1687 case 0x3B:
1688 case 0x3C:
1689 case 0x3E:
1690 case 0x3D:
1691 case 0x3F:
1692 case 0x40:
1693 case 0x5B:
1694 case 0x5D:
1695 case 0x5C:
1696 case 0x5E:
1697 case 0x60:
1698 case 0x7B:
1699 case 0x7D:
1700 return true;
1701 }
1702 return false;
1703}
1704
11fdf7f2 1705void url_encode(const string& src, string& dst, bool encode_slash)
7c673cae
FG
1706{
1707 const char *p = src.c_str();
1708 for (unsigned i = 0; i < src.size(); i++, p++) {
11fdf7f2
TL
1709 if ((!encode_slash && *p == 0x2F) || !char_needs_url_encoding(*p)) {
1710 dst.append(p, 1);
1711 }else {
7c673cae 1712 rgw_uri_escape_char(*p, dst);
7c673cae 1713 }
7c673cae
FG
1714 }
1715}
1716
11fdf7f2 1717std::string url_encode(const std::string& src, bool encode_slash)
7c673cae
FG
1718{
1719 std::string dst;
11fdf7f2 1720 url_encode(src, dst, encode_slash);
7c673cae
FG
1721
1722 return dst;
1723}
1724
f91f0fd5
TL
1725std::string url_remove_prefix(const std::string& url)
1726{
1727 std::string dst = url;
1728 auto pos = dst.find("http://");
1729 if (pos == std::string::npos) {
1730 pos = dst.find("https://");
1731 if (pos != std::string::npos) {
1732 dst.erase(pos, 8);
1733 } else {
1734 pos = dst.find("www.");
1735 if (pos != std::string::npos) {
1736 dst.erase(pos, 4);
1737 }
1738 }
1739 } else {
1740 dst.erase(pos, 7);
1741 }
1742
1743 return dst;
1744}
1745
7c673cae
FG
1746string rgw_trim_whitespace(const string& src)
1747{
1748 if (src.empty()) {
1749 return string();
1750 }
1751
1752 int start = 0;
1753 for (; start != (int)src.size(); start++) {
1754 if (!isspace(src[start]))
1755 break;
1756 }
1757
1758 int end = src.size() - 1;
1759 if (end < start) {
1760 return string();
1761 }
1762
1763 for (; end > start; end--) {
1764 if (!isspace(src[end]))
1765 break;
1766 }
1767
1768 return src.substr(start, end - start + 1);
1769}
1770
f67539c2 1771std::string_view rgw_trim_whitespace(const std::string_view& src)
7c673cae 1772{
f67539c2 1773 std::string_view res = src;
7c673cae
FG
1774
1775 while (res.size() > 0 && std::isspace(res.front())) {
1776 res.remove_prefix(1);
1777 }
1778 while (res.size() > 0 && std::isspace(res.back())) {
1779 res.remove_suffix(1);
1780 }
1781 return res;
1782}
1783
1784string rgw_trim_quotes(const string& val)
1785{
1786 string s = rgw_trim_whitespace(val);
1787 if (s.size() < 2)
1788 return s;
1789
1790 int start = 0;
1791 int end = s.size() - 1;
1792 int quotes_count = 0;
1793
1794 if (s[start] == '"') {
1795 start++;
1796 quotes_count++;
1797 }
1798 if (s[end] == '"') {
1799 end--;
1800 quotes_count++;
1801 }
1802 if (quotes_count == 2) {
1803 return s.substr(start, end - start + 1);
1804 }
1805 return s;
1806}
1807
7c673cae
FG
1808static struct rgw_name_to_flag cap_names[] = { {"*", RGW_CAP_ALL},
1809 {"read", RGW_CAP_READ},
1810 {"write", RGW_CAP_WRITE},
1811 {NULL, 0} };
1812
1813int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
1814{
9f95a23c 1815 return rgw_parse_list_of_flags(cap_names, str, perm);
7c673cae
FG
1816}
1817
1818int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm)
1819{
1820 int pos = cap.find('=');
1821 if (pos >= 0) {
31f18b77 1822 type = rgw_trim_whitespace(cap.substr(0, pos));
7c673cae
FG
1823 }
1824
1825 if (!is_valid_cap_type(type))
1826 return -ERR_INVALID_CAP;
1827
1828 string cap_perm;
1829 uint32_t perm = 0;
1830 if (pos < (int)cap.size() - 1) {
1831 cap_perm = cap.substr(pos + 1);
1832 int r = RGWUserCaps::parse_cap_perm(cap_perm, &perm);
1833 if (r < 0)
1834 return r;
1835 }
1836
1837 *pperm = perm;
1838
1839 return 0;
1840}
1841
1842int RGWUserCaps::add_cap(const string& cap)
1843{
1844 uint32_t perm;
1845 string type;
1846
1847 int r = get_cap(cap, type, &perm);
1848 if (r < 0)
1849 return r;
1850
1851 caps[type] |= perm;
1852
1853 return 0;
1854}
1855
1856int RGWUserCaps::remove_cap(const string& cap)
1857{
1858 uint32_t perm;
1859 string type;
1860
1861 int r = get_cap(cap, type, &perm);
1862 if (r < 0)
1863 return r;
1864
1865 map<string, uint32_t>::iterator iter = caps.find(type);
1866 if (iter == caps.end())
1867 return 0;
1868
1869 uint32_t& old_perm = iter->second;
1870 old_perm &= ~perm;
1871 if (!old_perm)
1872 caps.erase(iter);
1873
1874 return 0;
1875}
1876
1877int RGWUserCaps::add_from_string(const string& str)
1878{
1879 int start = 0;
1880 do {
31f18b77
FG
1881 auto end = str.find(';', start);
1882 if (end == string::npos)
7c673cae
FG
1883 end = str.size();
1884
1885 int r = add_cap(str.substr(start, end - start));
1886 if (r < 0)
1887 return r;
1888
1889 start = end + 1;
1890 } while (start < (int)str.size());
1891
1892 return 0;
1893}
1894
1895int RGWUserCaps::remove_from_string(const string& str)
1896{
1897 int start = 0;
1898 do {
31f18b77
FG
1899 auto end = str.find(';', start);
1900 if (end == string::npos)
7c673cae
FG
1901 end = str.size();
1902
1903 int r = remove_cap(str.substr(start, end - start));
1904 if (r < 0)
1905 return r;
1906
1907 start = end + 1;
1908 } while (start < (int)str.size());
1909
1910 return 0;
1911}
1912
1913void RGWUserCaps::dump(Formatter *f) const
1914{
1915 dump(f, "caps");
1916}
1917
1918void RGWUserCaps::dump(Formatter *f, const char *name) const
1919{
1920 f->open_array_section(name);
1921 map<string, uint32_t>::const_iterator iter;
1922 for (iter = caps.begin(); iter != caps.end(); ++iter)
1923 {
1924 f->open_object_section("cap");
1925 f->dump_string("type", iter->first);
1926 uint32_t perm = iter->second;
1927 string perm_str;
1928 for (int i=0; cap_names[i].type_name; i++) {
1929 if ((perm & cap_names[i].flag) == cap_names[i].flag) {
1930 if (perm_str.size())
1931 perm_str.append(", ");
1932
1933 perm_str.append(cap_names[i].type_name);
1934 perm &= ~cap_names[i].flag;
1935 }
1936 }
1937 if (perm_str.empty())
1938 perm_str = "<none>";
1939
1940 f->dump_string("perm", perm_str);
1941 f->close_section();
1942 }
1943
1944 f->close_section();
1945}
1946
1947struct RGWUserCap {
1948 string type;
1949 uint32_t perm;
1950
1951 void decode_json(JSONObj *obj) {
1952 JSONDecoder::decode_json("type", type, obj);
1953 string perm_str;
1954 JSONDecoder::decode_json("perm", perm_str, obj);
1955 if (RGWUserCaps::parse_cap_perm(perm_str, &perm) < 0) {
1956 throw JSONDecoder::err("failed to parse permissions");
1957 }
1958 }
1959};
1960
1961void RGWUserCaps::decode_json(JSONObj *obj)
1962{
1963 list<RGWUserCap> caps_list;
1964 decode_json_obj(caps_list, obj);
1965
1966 list<RGWUserCap>::iterator iter;
1967 for (iter = caps_list.begin(); iter != caps_list.end(); ++iter) {
1968 RGWUserCap& cap = *iter;
1969 caps[cap.type] = cap.perm;
1970 }
1971}
1972
9f95a23c 1973int RGWUserCaps::check_cap(const string& cap, uint32_t perm) const
7c673cae 1974{
9f95a23c 1975 auto iter = caps.find(cap);
7c673cae
FG
1976
1977 if ((iter == caps.end()) ||
1978 (iter->second & perm) != perm) {
1979 return -EPERM;
1980 }
1981
1982 return 0;
1983}
1984
1985bool RGWUserCaps::is_valid_cap_type(const string& tp)
1986{
1987 static const char *cap_type[] = { "user",
1988 "users",
1989 "buckets",
1990 "metadata",
1991 "usage",
1992 "zone",
1993 "bilog",
1994 "mdlog",
1995 "datalog",
11fdf7f2 1996 "roles",
f6b5b4d7 1997 "user-policy",
f91f0fd5
TL
1998 "amz-cache",
1999 "oidc-provider"};
7c673cae
FG
2000
2001 for (unsigned int i = 0; i < sizeof(cap_type) / sizeof(char *); ++i) {
2002 if (tp.compare(cap_type[i]) == 0) {
2003 return true;
2004 }
2005 }
2006
2007 return false;
2008}
2009
7c673cae
FG
2010void rgw_pool::from_str(const string& s)
2011{
1adf2230 2012 size_t pos = rgw_unescape_str(s, 0, '\\', ':', &name);
7c673cae 2013 if (pos != string::npos) {
1adf2230 2014 pos = rgw_unescape_str(s, pos, '\\', ':', &ns);
7c673cae
FG
2015 /* ignore return; if pos != string::npos it means that we had a colon
2016 * in the middle of ns that wasn't escaped, we're going to stop there
2017 */
2018 }
2019}
2020
2021string rgw_pool::to_str() const
2022{
2023 string esc_name;
1adf2230 2024 rgw_escape_str(name, '\\', ':', &esc_name);
7c673cae
FG
2025 if (ns.empty()) {
2026 return esc_name;
2027 }
2028 string esc_ns;
1adf2230 2029 rgw_escape_str(ns, '\\', ':', &esc_ns);
7c673cae
FG
2030 return esc_name + ":" + esc_ns;
2031}
2032
11fdf7f2 2033void rgw_raw_obj::decode_from_rgw_obj(bufferlist::const_iterator& bl)
7c673cae 2034{
11fdf7f2 2035 using ceph::decode;
7c673cae 2036 rgw_obj old_obj;
11fdf7f2 2037 decode(old_obj, bl);
7c673cae
FG
2038
2039 get_obj_bucket_and_oid_loc(old_obj, oid, loc);
2040 pool = old_obj.get_explicit_data_pool();
2041}
2042
7c673cae
FG
2043static struct rgw_name_to_flag op_type_mapping[] = { {"*", RGW_OP_TYPE_ALL},
2044 {"read", RGW_OP_TYPE_READ},
2045 {"write", RGW_OP_TYPE_WRITE},
2046 {"delete", RGW_OP_TYPE_DELETE},
2047 {NULL, 0} };
2048
2049
2050int rgw_parse_op_type_list(const string& str, uint32_t *perm)
2051{
9f95a23c 2052 return rgw_parse_list_of_flags(op_type_mapping, str, perm);
7c673cae
FG
2053}
2054
f67539c2 2055bool match_policy(std::string_view pattern, std::string_view input,
d2e6a577 2056 uint32_t flag)
7c673cae 2057{
d2e6a577
FG
2058 const uint32_t flag2 = flag & (MATCH_POLICY_ACTION|MATCH_POLICY_ARN) ?
2059 MATCH_CASE_INSENSITIVE : 0;
f64942e4
AA
2060 const bool colonblocks = !(flag & (MATCH_POLICY_RESOURCE |
2061 MATCH_POLICY_STRING));
7c673cae 2062
f67539c2
TL
2063 const auto npos = std::string_view::npos;
2064 std::string_view::size_type last_pos_input = 0, last_pos_pattern = 0;
d2e6a577 2065 while (true) {
f64942e4
AA
2066 auto cur_pos_input = colonblocks ? input.find(":", last_pos_input) : npos;
2067 auto cur_pos_pattern =
2068 colonblocks ? pattern.find(":", last_pos_pattern) : npos;
7c673cae 2069
d2e6a577
FG
2070 auto substr_input = input.substr(last_pos_input, cur_pos_input);
2071 auto substr_pattern = pattern.substr(last_pos_pattern, cur_pos_pattern);
7c673cae 2072
d2e6a577
FG
2073 if (!match_wildcards(substr_pattern, substr_input, flag2))
2074 return false;
2075
2076 if (cur_pos_pattern == npos)
2077 return cur_pos_input == npos;
2078 if (cur_pos_input == npos)
2079 return false;
7c673cae
FG
2080
2081 last_pos_pattern = cur_pos_pattern + 1;
2082 last_pos_input = cur_pos_input + 1;
2083 }
2084}
11fdf7f2
TL
2085
2086/*
2087 * make attrs look-like-this
2088 * converts underscores to dashes
2089 */
2090string lowercase_dash_http_attr(const string& orig)
2091{
2092 const char *s = orig.c_str();
2093 char buf[orig.size() + 1];
2094 buf[orig.size()] = '\0';
2095
2096 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2097 switch (*s) {
2098 case '_':
2099 buf[i] = '-';
2100 break;
2101 default:
2102 buf[i] = tolower(*s);
2103 }
2104 }
2105 return string(buf);
2106}
2107
2108/*
2109 * make attrs Look-Like-This
2110 * converts underscores to dashes
2111 */
2112string camelcase_dash_http_attr(const string& orig)
2113{
2114 const char *s = orig.c_str();
2115 char buf[orig.size() + 1];
2116 buf[orig.size()] = '\0';
2117
2118 bool last_sep = true;
2119
2120 for (size_t i = 0; i < orig.size(); ++i, ++s) {
2121 switch (*s) {
2122 case '_':
2123 case '-':
2124 buf[i] = '-';
2125 last_sep = true;
2126 break;
2127 default:
2128 if (last_sep) {
2129 buf[i] = toupper(*s);
2130 } else {
2131 buf[i] = tolower(*s);
2132 }
2133 last_sep = false;
2134 }
2135 }
2136 return string(buf);
2137}
9f95a23c
TL
2138
2139RGWBucketInfo::RGWBucketInfo()
2140{
2141}
2142
2143RGWBucketInfo::~RGWBucketInfo()
2144{
2145}
2146
2147void RGWBucketInfo::encode(bufferlist& bl) const {
f67539c2 2148 ENCODE_START(23, 4, bl);
9f95a23c
TL
2149 encode(bucket, bl);
2150 encode(owner.id, bl);
2151 encode(flags, bl);
2152 encode(zonegroup, bl);
2153 uint64_t ct = real_clock::to_time_t(creation_time);
2154 encode(ct, bl);
2155 encode(placement_rule, bl);
2156 encode(has_instance_obj, bl);
2157 encode(quota, bl);
9f95a23c
TL
2158 encode(requester_pays, bl);
2159 encode(owner.tenant, bl);
2160 encode(has_website, bl);
2161 if (has_website) {
2162 encode(website_conf, bl);
2163 }
9f95a23c
TL
2164 encode(swift_versioning, bl);
2165 if (swift_versioning) {
2166 encode(swift_ver_location, bl);
2167 }
2168 encode(creation_time, bl);
2169 encode(mdsearch_config, bl);
2170 encode(reshard_status, bl);
2171 encode(new_bucket_instance_id, bl);
2172 if (obj_lock_enabled()) {
2173 encode(obj_lock, bl);
2174 }
2175 bool has_sync_policy = !empty_sync_policy();
2176 encode(has_sync_policy, bl);
2177 if (has_sync_policy) {
2178 encode(*sync_policy, bl);
2179 }
f67539c2
TL
2180 encode(layout, bl);
2181 encode(owner.ns, bl);
9f95a23c
TL
2182 ENCODE_FINISH(bl);
2183}
2184
2185void RGWBucketInfo::decode(bufferlist::const_iterator& bl) {
f67539c2 2186 DECODE_START_LEGACY_COMPAT_LEN_32(23, 4, 4, bl);
9f95a23c
TL
2187 decode(bucket, bl);
2188 if (struct_v >= 2) {
2189 string s;
2190 decode(s, bl);
2191 owner.from_str(s);
2192 }
2193 if (struct_v >= 3)
2194 decode(flags, bl);
2195 if (struct_v >= 5)
2196 decode(zonegroup, bl);
2197 if (struct_v >= 6) {
2198 uint64_t ct;
2199 decode(ct, bl);
2200 if (struct_v < 17)
2201 creation_time = ceph::real_clock::from_time_t((time_t)ct);
2202 }
2203 if (struct_v >= 7)
2204 decode(placement_rule, bl);
2205 if (struct_v >= 8)
2206 decode(has_instance_obj, bl);
2207 if (struct_v >= 9)
2208 decode(quota, bl);
f67539c2
TL
2209 static constexpr uint8_t new_layout_v = 22;
2210 if (struct_v >= 10 && struct_v < new_layout_v)
2211 decode(layout.current_index.layout.normal.num_shards, bl);
2212 if (struct_v >= 11 && struct_v < new_layout_v)
2213 decode(layout.current_index.layout.normal.hash_type, bl);
9f95a23c
TL
2214 if (struct_v >= 12)
2215 decode(requester_pays, bl);
2216 if (struct_v >= 13)
2217 decode(owner.tenant, bl);
2218 if (struct_v >= 14) {
2219 decode(has_website, bl);
2220 if (has_website) {
2221 decode(website_conf, bl);
2222 } else {
2223 website_conf = RGWBucketWebsiteConf();
2224 }
2225 }
f67539c2 2226 if (struct_v >= 15 && struct_v < new_layout_v) {
9f95a23c
TL
2227 uint32_t it;
2228 decode(it, bl);
f67539c2 2229 layout.current_index.layout.type = (rgw::BucketIndexType)it;
9f95a23c 2230 } else {
f67539c2 2231 layout.current_index.layout.type = rgw::BucketIndexType::Normal;
9f95a23c
TL
2232 }
2233 swift_versioning = false;
2234 swift_ver_location.clear();
2235 if (struct_v >= 16) {
2236 decode(swift_versioning, bl);
2237 if (swift_versioning) {
2238 decode(swift_ver_location, bl);
2239 }
2240 }
2241 if (struct_v >= 17) {
2242 decode(creation_time, bl);
2243 }
2244 if (struct_v >= 18) {
2245 decode(mdsearch_config, bl);
2246 }
2247 if (struct_v >= 19) {
2248 decode(reshard_status, bl);
2249 decode(new_bucket_instance_id, bl);
2250 }
2251 if (struct_v >= 20 && obj_lock_enabled()) {
2252 decode(obj_lock, bl);
2253 }
2254 if (struct_v >= 21) {
2255 decode(sync_policy, bl);
2256 }
f67539c2
TL
2257 if (struct_v >= 22) {
2258 decode(layout, bl);
2259 }
2260 if (struct_v >= 23) {
2261 decode(owner.ns, bl);
2262 }
a4b75251
TL
2263
2264 if (layout.logs.empty() &&
2265 layout.current_index.layout.type == rgw::BucketIndexType::Normal) {
2266 layout.logs.push_back(rgw::log_layout_from_index(0, layout.current_index.layout.normal));
2267 }
9f95a23c
TL
2268 DECODE_FINISH(bl);
2269}
2270
2271void RGWBucketInfo::set_sync_policy(rgw_sync_policy_info&& policy)
2272{
2273 sync_policy = std::move(policy);
2274}
2275
2276bool RGWBucketInfo::empty_sync_policy() const
2277{
2278 if (!sync_policy) {
2279 return true;
2280 }
2281
2282 return sync_policy->empty();
2283}
2284