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