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