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