1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
8 #include <boost/tokenizer.hpp>
9 #include <boost/algorithm/string.hpp>
10 #include <boost/utility/string_view.hpp>
12 #include "json_spirit/json_spirit.h"
13 #include "common/ceph_json.h"
16 #include "rgw_common.h"
18 #include "rgw_string.h"
19 #include "rgw_rados.h"
20 #include "rgw_http_errors.h"
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"
27 #include "common/perf_counters.h"
28 #include "common/strtol.h"
29 #include "include/str_list.h"
30 #include "auth/Crypto.h"
31 #include "rgw_crypt_sanitize.h"
35 #define dout_context g_ceph_context
36 #define dout_subsys ceph_subsys_rgw
39 using boost::optional
;
42 using rgw::IAM::Effect
;
43 using rgw::IAM::op_to_perm
;
44 using rgw::IAM::Policy
;
46 PerfCounters
*perfcounter
= NULL
;
48 const uint32_t RGWBucketInfo::NUM_SHARDS_BLIND_BUCKET(UINT32_MAX
);
50 rgw_http_errors
rgw_http_s3_errors({
52 { STATUS_CREATED
, {201, "Created" }},
53 { STATUS_ACCEPTED
, {202, "Accepted" }},
54 { STATUS_NO_CONTENT
, {204, "NoContent" }},
55 { STATUS_PARTIAL_CONTENT
, {206, "" }},
56 { ERR_PERMANENT_REDIRECT
, {301, "PermanentRedirect" }},
57 { ERR_WEBSITE_REDIRECT
, {301, "WebsiteRedirect" }},
58 { STATUS_REDIRECT
, {303, "" }},
59 { ERR_NOT_MODIFIED
, {304, "NotModified" }},
60 { EINVAL
, {400, "InvalidArgument" }},
61 { ERR_INVALID_REQUEST
, {400, "InvalidRequest" }},
62 { ERR_INVALID_DIGEST
, {400, "InvalidDigest" }},
63 { ERR_BAD_DIGEST
, {400, "BadDigest" }},
64 { ERR_INVALID_LOCATION_CONSTRAINT
, {400, "InvalidLocationConstraint" }},
65 { ERR_ZONEGROUP_DEFAULT_PLACEMENT_MISCONFIGURATION
, {400, "ZonegroupDefaultPlacementMisconfiguration" }},
66 { ERR_INVALID_BUCKET_NAME
, {400, "InvalidBucketName" }},
67 { ERR_INVALID_OBJECT_NAME
, {400, "InvalidObjectName" }},
68 { ERR_UNRESOLVABLE_EMAIL
, {400, "UnresolvableGrantByEmailAddress" }},
69 { ERR_INVALID_PART
, {400, "InvalidPart" }},
70 { ERR_INVALID_PART_ORDER
, {400, "InvalidPartOrder" }},
71 { ERR_REQUEST_TIMEOUT
, {400, "RequestTimeout" }},
72 { ERR_TOO_LARGE
, {400, "EntityTooLarge" }},
73 { ERR_TOO_SMALL
, {400, "EntityTooSmall" }},
74 { ERR_TOO_MANY_BUCKETS
, {400, "TooManyBuckets" }},
75 { ERR_MALFORMED_XML
, {400, "MalformedXML" }},
76 { ERR_AMZ_CONTENT_SHA256_MISMATCH
, {400, "XAmzContentSHA256Mismatch" }},
77 { ERR_INVALID_TAG
, {400, "InvalidTag"}},
78 { ERR_MALFORMED_ACL_ERROR
, {400, "MalformedACLError" }},
79 { ERR_INVALID_ENCRYPTION_ALGORITHM
, {400, "InvalidEncryptionAlgorithmError" }},
80 { ERR_LENGTH_REQUIRED
, {411, "MissingContentLength" }},
81 { EACCES
, {403, "AccessDenied" }},
82 { EPERM
, {403, "AccessDenied" }},
83 { ERR_SIGNATURE_NO_MATCH
, {403, "SignatureDoesNotMatch" }},
84 { ERR_INVALID_ACCESS_KEY
, {403, "InvalidAccessKeyId" }},
85 { ERR_USER_SUSPENDED
, {403, "UserSuspended" }},
86 { ERR_REQUEST_TIME_SKEWED
, {403, "RequestTimeTooSkewed" }},
87 { ERR_QUOTA_EXCEEDED
, {403, "QuotaExceeded" }},
88 { ENOENT
, {404, "NoSuchKey" }},
89 { ERR_NO_SUCH_BUCKET
, {404, "NoSuchBucket" }},
90 { ERR_NO_SUCH_WEBSITE_CONFIGURATION
, {404, "NoSuchWebsiteConfiguration" }},
91 { ERR_NO_SUCH_UPLOAD
, {404, "NoSuchUpload" }},
92 { ERR_NOT_FOUND
, {404, "Not Found"}},
93 { ERR_NO_SUCH_LC
, {404, "NoSuchLifecycleConfiguration"}},
94 { ERR_NO_SUCH_BUCKET_POLICY
, {404, "NoSuchBucketPolicy"}},
95 { ERR_NO_SUCH_USER
, {404, "NoSuchUser"}},
96 { ERR_NO_SUCH_SUBUSER
, {404, "NoSuchSubUser"}},
97 { ERR_NO_SUCH_CORS_CONFIGURATION
, {404, "NoSuchCORSConfiguration"}},
98 { ERR_METHOD_NOT_ALLOWED
, {405, "MethodNotAllowed" }},
99 { ETIMEDOUT
, {408, "RequestTimeout" }},
100 { EEXIST
, {409, "BucketAlreadyExists" }},
101 { ERR_USER_EXIST
, {409, "UserAlreadyExists" }},
102 { ERR_EMAIL_EXIST
, {409, "EmailExists" }},
103 { ERR_KEY_EXIST
, {409, "KeyExists"}},
104 { ERR_TAG_CONFLICT
, {409, "OperationAborted"}},
105 { ERR_INVALID_SECRET_KEY
, {400, "InvalidSecretKey"}},
106 { ERR_INVALID_KEY_TYPE
, {400, "InvalidKeyType"}},
107 { ERR_INVALID_CAP
, {400, "InvalidCapability"}},
108 { ERR_INVALID_TENANT_NAME
, {400, "InvalidTenantName" }},
109 { ENOTEMPTY
, {409, "BucketNotEmpty" }},
110 { ERR_PRECONDITION_FAILED
, {412, "PreconditionFailed" }},
111 { ERANGE
, {416, "InvalidRange" }},
112 { ERR_UNPROCESSABLE_ENTITY
, {422, "UnprocessableEntity" }},
113 { ERR_LOCKED
, {423, "Locked" }},
114 { ERR_INTERNAL_ERROR
, {500, "InternalError" }},
115 { ERR_NOT_IMPLEMENTED
, {501, "NotImplemented" }},
116 { ERR_SERVICE_UNAVAILABLE
, {503, "ServiceUnavailable"}},
117 { ERR_ZERO_IN_URL
, {400, "InvalidRequest" }},
120 rgw_http_errors
rgw_http_swift_errors({
121 { EACCES
, {403, "AccessDenied" }},
122 { EPERM
, {401, "AccessDenied" }},
123 { ENAMETOOLONG
, {400, "Metadata name too long" }},
124 { ERR_USER_SUSPENDED
, {401, "UserSuspended" }},
125 { ERR_INVALID_UTF8
, {412, "Invalid UTF8" }},
126 { ERR_BAD_URL
, {412, "Bad URL" }},
127 { ERR_NOT_SLO_MANIFEST
, {400, "Not an SLO manifest" }},
128 { ERR_QUOTA_EXCEEDED
, {413, "QuotaExceeded" }},
129 { ENOTEMPTY
, {409, "There was a conflict when trying "
130 "to complete your request." }},
131 /* FIXME(rzarzynski): we need to find a way to apply Swift's error handling
132 * procedures also for ERR_ZERO_IN_URL. This make a problem as the validation
133 * is performed very early, even before setting the req_state::proto_flags. */
134 { ERR_ZERO_IN_URL
, {412, "Invalid UTF8 or contains NULL"}},
137 int rgw_perf_start(CephContext
*cct
)
139 PerfCountersBuilder
plb(cct
, "rgw", l_rgw_first
, l_rgw_last
);
141 // RGW emits comparatively few metrics, so let's be generous
142 // and mark them all USEFUL to get transmission to ceph-mgr by default.
143 plb
.set_prio_default(PerfCountersBuilder::PRIO_USEFUL
);
145 plb
.add_u64_counter(l_rgw_req
, "req", "Requests");
146 plb
.add_u64_counter(l_rgw_failed_req
, "failed_req", "Aborted requests");
148 plb
.add_u64_counter(l_rgw_get
, "get", "Gets");
149 plb
.add_u64_counter(l_rgw_get_b
, "get_b", "Size of gets");
150 plb
.add_time_avg(l_rgw_get_lat
, "get_initial_lat", "Get latency");
151 plb
.add_u64_counter(l_rgw_put
, "put", "Puts");
152 plb
.add_u64_counter(l_rgw_put_b
, "put_b", "Size of puts");
153 plb
.add_time_avg(l_rgw_put_lat
, "put_initial_lat", "Put latency");
155 plb
.add_u64(l_rgw_qlen
, "qlen", "Queue length");
156 plb
.add_u64(l_rgw_qactive
, "qactive", "Active requests queue");
158 plb
.add_u64_counter(l_rgw_cache_hit
, "cache_hit", "Cache hits");
159 plb
.add_u64_counter(l_rgw_cache_miss
, "cache_miss", "Cache miss");
161 plb
.add_u64_counter(l_rgw_keystone_token_cache_hit
, "keystone_token_cache_hit", "Keystone token cache hits");
162 plb
.add_u64_counter(l_rgw_keystone_token_cache_miss
, "keystone_token_cache_miss", "Keystone token cache miss");
164 perfcounter
= plb
.create_perf_counters();
165 cct
->get_perfcounters_collection()->add(perfcounter
);
169 void rgw_perf_stop(CephContext
*cct
)
172 cct
->get_perfcounters_collection()->remove(perfcounter
);
176 using namespace ceph::crypto
;
195 return (http_ret
== 200);
201 return !(http_ret
>= 200 && http_ret
<= 399);
204 // The requestURI transferred from the frontend can be abs_path or absoluteURI
205 // If it is absoluteURI, we should adjust it to abs_path for the following
206 // S3 authorization and some other processes depending on the requestURI
207 // The absoluteURI can start with "http://", "https://", "ws://" or "wss://"
208 static string
get_abs_path(const string
& request_uri
) {
209 const static string ABS_PREFIXS
[] = {"http://", "https://", "ws://", "wss://"};
211 for (int i
= 0; i
< 4; ++i
) {
212 if (boost::algorithm::starts_with(request_uri
, ABS_PREFIXS
[i
])) {
217 if (!isAbs
) { // it is not a valid absolute uri
220 size_t beg_pos
= request_uri
.find("://") + 3;
221 size_t len
= request_uri
.size();
222 beg_pos
= request_uri
.find('/', beg_pos
);
223 if (beg_pos
== string::npos
) return request_uri
;
224 return request_uri
.substr(beg_pos
, len
- beg_pos
);
227 req_info::req_info(CephContext
*cct
, const class RGWEnv
*env
) : env(env
) {
228 method
= env
->get("REQUEST_METHOD", "");
229 script_uri
= env
->get("SCRIPT_URI", cct
->_conf
->rgw_script_uri
.c_str());
230 request_uri
= env
->get("REQUEST_URI", cct
->_conf
->rgw_request_uri
.c_str());
231 if (request_uri
[0] != '/') {
232 request_uri
= get_abs_path(request_uri
);
234 auto pos
= request_uri
.find('?');
235 if (pos
!= string::npos
) {
236 request_params
= request_uri
.substr(pos
+ 1);
237 request_uri
= request_uri
.substr(0, pos
);
239 request_params
= env
->get("QUERY_STRING", "");
241 host
= env
->get("HTTP_HOST", "");
243 // strip off any trailing :port from host (added by CrossFTP and maybe others)
244 size_t colon_offset
= host
.find_last_of(':');
245 if (colon_offset
!= string::npos
) {
246 bool all_digits
= true;
247 for (unsigned i
= colon_offset
+ 1; i
< host
.size(); ++i
) {
248 if (!isdigit(host
[i
])) {
254 host
.resize(colon_offset
);
259 void req_info::rebuild_from(req_info
& src
)
262 script_uri
= src
.script_uri
;
264 if (src
.effective_uri
.empty()) {
265 request_uri
= src
.request_uri
;
267 request_uri
= src
.effective_uri
;
269 effective_uri
.clear();
272 x_meta_map
= src
.x_meta_map
;
273 x_meta_map
.erase("x-amz-date");
277 req_state::req_state(CephContext
* _cct
, RGWEnv
* e
, RGWUserInfo
* u
)
278 : cct(_cct
), cio(NULL
), op(OP_UNKNOWN
), user(u
), has_acl_header(false),
281 enable_ops_log
= e
->conf
.enable_ops_log
;
282 enable_usage_log
= e
->conf
.enable_usage_log
;
283 defer_to_bucket_acls
= e
->conf
.defer_to_bucket_acls
;
284 content_started
= false;
292 system_request
= false;
294 time
= ceph_clock_now();
296 bucket_instance_shard_id
= -1;
298 bucket_exists
= false;
299 has_bad_meta
= false;
301 local_source
= false;
306 req_state::~req_state() {
310 bool search_err(rgw_http_errors
& errs
, int err_no
, bool is_website_redirect
, int& http_ret
, string
& code
)
312 auto r
= errs
.find(err_no
);
313 if (r
!= errs
.end()) {
314 if (! is_website_redirect
)
315 http_ret
= r
->second
.first
;
316 code
= r
->second
.second
;
322 void set_req_state_err(struct rgw_err
& err
, /* out */
324 const int prot_flags
) /* in */
330 bool is_website_redirect
= false;
332 if (prot_flags
& RGW_REST_SWIFT
) {
333 if (search_err(rgw_http_swift_errors
, err_no
, is_website_redirect
, err
.http_ret
, err
.err_code
))
337 //Default to searching in s3 errors
338 is_website_redirect
|= (prot_flags
& RGW_REST_WEBSITE
)
339 && err_no
== ERR_WEBSITE_REDIRECT
&& err
.is_clear();
340 if (search_err(rgw_http_s3_errors
, err_no
, is_website_redirect
, err
.http_ret
, err
.err_code
))
342 dout(0) << "WARNING: set_req_state_err err_no=" << err_no
343 << " resorting to 500" << dendl
;
346 err
.err_code
= "UnknownError";
349 void set_req_state_err(struct req_state
* s
, int err_no
, const string
& err_msg
)
352 set_req_state_err(s
, err_no
);
353 if (s
->prot_flags
& RGW_REST_SWIFT
&& !err_msg
.empty()) {
354 /* TODO(rzarzynski): there never ever should be a check like this one.
355 * It's here only for the sake of the patch's backportability. Further
356 * commits will move the logic to a per-RGWHandler replacement of
357 * the end_header() function. Alternativaly, we might consider making
358 * that just for the dump(). Please take a look on @cbodley's comments
359 * in PR #10690 (https://github.com/ceph/ceph/pull/10690). */
360 s
->err
.err_code
= err_msg
;
362 s
->err
.message
= err_msg
;
367 void set_req_state_err(struct req_state
* s
, int err_no
)
370 set_req_state_err(s
->err
, err_no
, s
->prot_flags
);
374 void dump(struct req_state
* s
)
376 if (s
->format
!= RGW_FORMAT_HTML
)
377 s
->formatter
->open_object_section("Error");
378 if (!s
->err
.err_code
.empty())
379 s
->formatter
->dump_string("Code", s
->err
.err_code
);
380 if (!s
->err
.message
.empty())
381 s
->formatter
->dump_string("Message", s
->err
.message
);
382 if (!s
->bucket_name
.empty()) // TODO: connect to expose_bucket
383 s
->formatter
->dump_string("BucketName", s
->bucket_name
);
384 if (!s
->trans_id
.empty()) // TODO: connect to expose_bucket or another toggle
385 s
->formatter
->dump_string("RequestId", s
->trans_id
);
386 s
->formatter
->dump_string("HostId", s
->host_id
);
387 if (s
->format
!= RGW_FORMAT_HTML
)
388 s
->formatter
->close_section();
396 #define STR_LEN_ENTRY(s) { s, sizeof(s) - 1 }
398 struct str_len meta_prefixes
[] = { STR_LEN_ENTRY("HTTP_X_AMZ"),
399 STR_LEN_ENTRY("HTTP_X_GOOG"),
400 STR_LEN_ENTRY("HTTP_X_DHO"),
401 STR_LEN_ENTRY("HTTP_X_RGW"),
402 STR_LEN_ENTRY("HTTP_X_OBJECT"),
403 STR_LEN_ENTRY("HTTP_X_CONTAINER"),
404 STR_LEN_ENTRY("HTTP_X_ACCOUNT"),
408 void req_info::init_meta_info(bool *found_bad_meta
)
412 for (const auto& kv
: env
->get_map()) {
414 const string
& header_name
= kv
.first
;
415 const string
& val
= kv
.second
;
416 for (int prefix_num
= 0; (prefix
= meta_prefixes
[prefix_num
].str
) != NULL
; prefix_num
++) {
417 int len
= meta_prefixes
[prefix_num
].len
;
418 const char *p
= header_name
.c_str();
419 if (strncmp(p
, prefix
, len
) == 0) {
420 dout(10) << "meta>> " << p
<< dendl
;
421 const char *name
= p
+len
; /* skip the prefix */
422 int name_len
= header_name
.size() - len
;
424 if (found_bad_meta
&& strncmp(name
, "_META_", name_len
) == 0)
425 *found_bad_meta
= true;
427 char name_low
[meta_prefixes
[0].len
+ name_len
+ 1];
428 snprintf(name_low
, meta_prefixes
[0].len
- 5 + name_len
+ 1, "%s%s", meta_prefixes
[0].str
+ 5 /* skip HTTP_ */, name
); // normalize meta prefix
430 for (j
= 0; name_low
[j
]; j
++) {
431 if (name_low
[j
] != '_')
432 name_low
[j
] = tolower(name_low
[j
]);
438 auto it
= x_meta_map
.find(name_low
);
439 if (it
!= x_meta_map
.end()) {
440 string old
= it
->second
;
441 boost::algorithm::trim_right(old
);
444 x_meta_map
[name_low
] = old
;
446 x_meta_map
[name_low
] = val
;
451 for (const auto& kv
: x_meta_map
) {
452 dout(10) << "x>> " << kv
.first
<< ":" << rgw::crypt_sanitize::x_meta_map
{kv
.first
, kv
.second
} << dendl
;
456 std::ostream
& operator<<(std::ostream
& oss
, const rgw_err
&err
)
458 oss
<< "rgw_err(http_ret=" << err
.http_ret
<< ", err_code='" << err
.err_code
<< "') ";
462 string
rgw_string_unquote(const string
& s
)
464 if (s
[0] != '"' || s
.size() < 2)
468 for (len
= s
.size(); len
> 2; --len
) {
469 if (s
[len
- 1] != ' ')
476 return s
.substr(1, len
- 2);
479 static bool check_str_end(const char *s
)
492 static bool check_gmt_end(const char *s
)
497 while (isspace(*s
)) {
501 /* check for correct timezone */
502 if ((strncmp(s
, "GMT", 3) != 0) &&
503 (strncmp(s
, "UTC", 3) != 0)) {
510 static bool parse_rfc850(const char *s
, struct tm
*t
)
512 memset(t
, 0, sizeof(*t
));
513 return check_gmt_end(strptime(s
, "%A, %d-%b-%y %H:%M:%S ", t
));
516 static bool parse_asctime(const char *s
, struct tm
*t
)
518 memset(t
, 0, sizeof(*t
));
519 return check_str_end(strptime(s
, "%a %b %d %H:%M:%S %Y", t
));
522 static bool parse_rfc1123(const char *s
, struct tm
*t
)
524 memset(t
, 0, sizeof(*t
));
525 return check_gmt_end(strptime(s
, "%a, %d %b %Y %H:%M:%S ", t
));
528 static bool parse_rfc1123_alt(const char *s
, struct tm
*t
)
530 memset(t
, 0, sizeof(*t
));
531 return check_str_end(strptime(s
, "%a, %d %b %Y %H:%M:%S %z", t
));
534 bool parse_rfc2616(const char *s
, struct tm
*t
)
536 return parse_rfc850(s
, t
) || parse_asctime(s
, t
) || parse_rfc1123(s
, t
) || parse_rfc1123_alt(s
,t
);
539 bool parse_iso8601(const char *s
, struct tm
*t
, uint32_t *pns
, bool extended_format
)
541 memset(t
, 0, sizeof(*t
));
547 if (extended_format
) {
548 p
= strptime(s
, "%Y-%m-%dT%T", t
);
550 p
= strptime(s
, "%Y-%m-%d %T", t
);
553 p
= strptime(s
, "%Y%m%dT%H%M%S", t
);
556 dout(0) << "parse_iso8601 failed" << dendl
;
559 const boost::string_view str
= rgw_trim_whitespace(boost::string_view(p
));
560 int len
= str
.size();
562 if (len
== 0 || (len
== 1 && str
[0] == 'Z'))
570 boost::string_view nsstr
= str
.substr(1, len
- 2);
571 int r
= stringtoul(nsstr
.to_string(), &ms
);
579 if (nsstr
.size() > 9) {
580 nsstr
= nsstr
.substr(0, 9);
583 uint64_t mul_table
[] = { 0,
595 *pns
= ms
* mul_table
[nsstr
.size()];
600 int parse_key_value(string
& in_str
, const char *delim
, string
& key
, string
& val
)
605 auto pos
= in_str
.find(delim
);
606 if (pos
== string::npos
)
609 key
= rgw_trim_whitespace(in_str
.substr(0, pos
));
610 val
= rgw_trim_whitespace(in_str
.substr(pos
+ 1));
615 int parse_key_value(string
& in_str
, string
& key
, string
& val
)
617 return parse_key_value(in_str
, "=", key
,val
);
620 boost::optional
<std::pair
<boost::string_view
, boost::string_view
>>
621 parse_key_value(const boost::string_view
& in_str
,
622 const boost::string_view
& delim
)
624 const size_t pos
= in_str
.find(delim
);
625 if (pos
== boost::string_view::npos
) {
629 const auto key
= rgw_trim_whitespace(in_str
.substr(0, pos
));
630 const auto val
= rgw_trim_whitespace(in_str
.substr(pos
+ 1));
632 return std::make_pair(key
, val
);
635 boost::optional
<std::pair
<boost::string_view
, boost::string_view
>>
636 parse_key_value(const boost::string_view
& in_str
)
638 return parse_key_value(in_str
, "=");
641 int parse_time(const char *time_str
, real_time
*time
)
646 if (!parse_rfc2616(time_str
, &tm
) && !parse_iso8601(time_str
, &tm
, &ns
)) {
650 time_t sec
= internal_timegm(&tm
);
651 *time
= utime_t(sec
, ns
).to_real_time();
656 #define TIME_BUF_SIZE 128
658 void rgw_to_iso8601(const real_time
& t
, char *dest
, int buf_size
)
662 char buf
[TIME_BUF_SIZE
];
664 time_t epoch
= ut
.sec();
665 struct tm
*tmp
= gmtime_r(&epoch
, &result
);
669 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T", tmp
) == 0)
672 snprintf(dest
, buf_size
, "%s.%03dZ", buf
, (int)(ut
.usec() / 1000));
675 void rgw_to_iso8601(const real_time
& t
, string
*dest
)
677 char buf
[TIME_BUF_SIZE
];
678 rgw_to_iso8601(t
, buf
, sizeof(buf
));
683 string
rgw_to_asctime(const utime_t
& t
)
691 * calculate the sha1 value of a given msg and key
693 void calc_hmac_sha1(const char *key
, int key_len
,
694 const char *msg
, int msg_len
, char *dest
)
695 /* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
697 HMACSHA1
hmac((const unsigned char *)key
, key_len
);
698 hmac
.Update((const unsigned char *)msg
, msg_len
);
699 hmac
.Final((unsigned char *)dest
);
703 * calculate the sha256 value of a given msg and key
705 void calc_hmac_sha256(const char *key
, int key_len
,
706 const char *msg
, int msg_len
, char *dest
)
708 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
710 HMACSHA256
hmac((const unsigned char *)key
, key_len
);
711 hmac
.Update((const unsigned char *)msg
, msg_len
);
712 hmac
.Final((unsigned char *)hash_sha256
);
714 memcpy(dest
, hash_sha256
, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
);
717 using ceph::crypto::SHA256
;
720 * calculate the sha256 hash value of a given msg
722 sha256_digest_t
calc_hash_sha256(const boost::string_view
& msg
)
724 std::array
<unsigned char, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
> hash
;
727 hasher
.Update(reinterpret_cast<const unsigned char*>(msg
.data()), msg
.size());
728 hasher
.Final(hash
.data());
733 SHA256
* calc_hash_sha256_open_stream()
738 void calc_hash_sha256_update_stream(SHA256
*hash
, const char *msg
, int len
)
740 hash
->Update((const unsigned char *)msg
, len
);
743 string
calc_hash_sha256_close_stream(SHA256
**phash
)
745 SHA256
*hash
= *phash
;
747 hash
= calc_hash_sha256_open_stream();
749 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
751 hash
->Final((unsigned char *)hash_sha256
);
753 char hex_str
[(CEPH_CRYPTO_SHA256_DIGESTSIZE
* 2) + 1];
754 buf_to_hex((unsigned char *)hash_sha256
, CEPH_CRYPTO_SHA256_DIGESTSIZE
, hex_str
);
759 return std::string(hex_str
);
762 std::string
calc_hash_sha256_restart_stream(SHA256
**phash
)
764 const auto hash
= calc_hash_sha256_close_stream(phash
);
765 *phash
= calc_hash_sha256_open_stream();
770 int gen_rand_base64(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
773 char tmp_dest
[size
+ 4]; /* so that there's space for the extra '=' characters, and some */
776 ret
= get_random_bytes(buf
, sizeof(buf
));
778 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
782 ret
= ceph_armor(tmp_dest
, &tmp_dest
[sizeof(tmp_dest
)],
783 (const char *)buf
, ((const char *)buf
) + ((size
- 1) * 3 + 4 - 1) / 4);
785 lderr(cct
) << "ceph_armor failed" << dendl
;
788 tmp_dest
[ret
] = '\0';
789 memcpy(dest
, tmp_dest
, size
);
795 static const char alphanum_upper_table
[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
797 int gen_rand_alphanumeric_upper(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
799 int ret
= get_random_bytes(dest
, size
);
801 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
806 for (i
=0; i
<size
- 1; i
++) {
807 int pos
= (unsigned)dest
[i
];
808 dest
[i
] = alphanum_upper_table
[pos
% (sizeof(alphanum_upper_table
) - 1)];
815 static const char alphanum_lower_table
[]="0123456789abcdefghijklmnopqrstuvwxyz";
817 int gen_rand_alphanumeric_lower(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
819 int ret
= get_random_bytes(dest
, size
);
821 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
826 for (i
=0; i
<size
- 1; i
++) {
827 int pos
= (unsigned)dest
[i
];
828 dest
[i
] = alphanum_lower_table
[pos
% (sizeof(alphanum_lower_table
) - 1)];
835 int gen_rand_alphanumeric_lower(CephContext
*cct
, string
*str
, int length
)
837 char buf
[length
+ 1];
838 int ret
= gen_rand_alphanumeric_lower(cct
, buf
, sizeof(buf
));
846 // this is basically a modified base64 charset, url friendly
847 static const char alphanum_table
[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
849 int gen_rand_alphanumeric(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
851 int ret
= get_random_bytes(dest
, size
);
853 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
858 for (i
=0; i
<size
- 1; i
++) {
859 int pos
= (unsigned)dest
[i
];
860 dest
[i
] = alphanum_table
[pos
& 63];
867 static const char alphanum_no_underscore_table
[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.";
869 int gen_rand_alphanumeric_no_underscore(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
871 int ret
= get_random_bytes(dest
, size
);
873 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
878 for (i
=0; i
<size
- 1; i
++) {
879 int pos
= (unsigned)dest
[i
];
880 dest
[i
] = alphanum_no_underscore_table
[pos
& 63];
887 static const char alphanum_plain_table
[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
889 int gen_rand_alphanumeric_plain(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
891 int ret
= get_random_bytes(dest
, size
);
893 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
898 for (i
=0; i
<size
- 1; i
++) {
899 int pos
= (unsigned)dest
[i
];
900 dest
[i
] = alphanum_plain_table
[pos
% (sizeof(alphanum_plain_table
) - 1)];
909 auto delim_pos
= str
.find('=');
912 if (delim_pos
== string::npos
) {
917 name
= str
.substr(0, delim_pos
);
918 val
= str
.substr(delim_pos
+ 1);
924 int RGWHTTPArgs::parse()
936 int fpos
= str
.find('&', pos
);
941 std::string nameval
= url_decode(str
.substr(pos
, fpos
- pos
), true);
942 NameVal
nv(std::move(nameval
));
943 int ret
= nv
.parse();
945 string
& name
= nv
.get_name();
946 string
& val
= nv
.get_val();
957 void RGWHTTPArgs::append(const string
& name
, const string
& val
)
959 if (name
.compare(0, sizeof(RGW_SYS_PARAM_PREFIX
) - 1, RGW_SYS_PARAM_PREFIX
) == 0) {
960 sys_val_map
[name
] = val
;
965 if ((name
.compare("acl") == 0) ||
966 (name
.compare("cors") == 0) ||
967 (name
.compare("location") == 0) ||
968 (name
.compare("logging") == 0) ||
969 (name
.compare("usage") == 0) ||
970 (name
.compare("lifecycle") == 0) ||
971 (name
.compare("delete") == 0) ||
972 (name
.compare("uploads") == 0) ||
973 (name
.compare("partNumber") == 0) ||
974 (name
.compare("uploadId") == 0) ||
975 (name
.compare("versionId") == 0) ||
976 (name
.compare("start-date") == 0) ||
977 (name
.compare("end-date") == 0) ||
978 (name
.compare("versions") == 0) ||
979 (name
.compare("versioning") == 0) ||
980 (name
.compare("website") == 0) ||
981 (name
.compare("requestPayment") == 0) ||
982 (name
.compare("torrent") == 0) ||
983 (name
.compare("tagging") == 0)) {
984 sub_resources
[name
] = val
;
985 } else if (name
[0] == 'r') { // root of all evil
986 if ((name
.compare("response-content-type") == 0) ||
987 (name
.compare("response-content-language") == 0) ||
988 (name
.compare("response-expires") == 0) ||
989 (name
.compare("response-cache-control") == 0) ||
990 (name
.compare("response-content-disposition") == 0) ||
991 (name
.compare("response-content-encoding") == 0)) {
992 sub_resources
[name
] = val
;
993 has_resp_modifier
= true;
995 } else if ((name
.compare("subuser") == 0) ||
996 (name
.compare("key") == 0) ||
997 (name
.compare("caps") == 0) ||
998 (name
.compare("index") == 0) ||
999 (name
.compare("policy") == 0) ||
1000 (name
.compare("quota") == 0) ||
1001 (name
.compare("object") == 0)) {
1003 if (!admin_subresource_added
) {
1004 sub_resources
[name
] = "";
1005 admin_subresource_added
= true;
1010 const string
& RGWHTTPArgs::get(const string
& name
, bool *exists
) const
1012 auto iter
= val_map
.find(name
);
1013 bool e
= (iter
!= std::end(val_map
));
1017 return iter
->second
;
1021 boost::optional
<const std::string
&>
1022 RGWHTTPArgs::get_optional(const std::string
& name
) const
1025 const std::string
& value
= get(name
, &exists
);
1033 int RGWHTTPArgs::get_bool(const string
& name
, bool *val
, bool *exists
)
1035 map
<string
, string
>::iterator iter
;
1036 iter
= val_map
.find(name
);
1037 bool e
= (iter
!= val_map
.end());
1042 const char *s
= iter
->second
.c_str();
1044 if (strcasecmp(s
, "false") == 0) {
1046 } else if (strcasecmp(s
, "true") == 0) {
1056 int RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool *exists
)
1059 return get_bool(s
, val
, exists
);
1062 void RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool def_val
)
1064 bool exists
= false;
1065 if ((get_bool(name
, val
, &exists
) < 0) ||
1071 string
RGWHTTPArgs::sys_get(const string
& name
, bool * const exists
) const
1073 const auto iter
= sys_val_map
.find(name
);
1074 const bool e
= (iter
!= sys_val_map
.end());
1080 return e
? iter
->second
: string();
1083 bool verify_user_permission(struct req_state
* const s
,
1084 RGWAccessControlPolicy
* const user_acl
,
1087 /* S3 doesn't support account ACLs. */
1091 if ((perm
& (int)s
->perm_mask
) != perm
)
1094 return user_acl
->verify_permission(*s
->auth
.identity
, perm
, perm
);
1097 bool verify_user_permission(struct req_state
* const s
,
1100 return verify_user_permission(s
, s
->user_acl
.get(), perm
);
1103 bool verify_requester_payer_permission(struct req_state
*s
)
1105 if (!s
->bucket_info
.requester_pays
)
1108 if (s
->auth
.identity
->is_owner_of(s
->bucket_info
.owner
))
1111 if (s
->auth
.identity
->is_anonymous()) {
1115 const char *request_payer
= s
->info
.env
->get("HTTP_X_AMZ_REQUEST_PAYER");
1116 if (!request_payer
) {
1118 request_payer
= s
->info
.args
.get("x-amz-request-payer", &exists
).c_str();
1124 if (strcasecmp(request_payer
, "requester") == 0) {
1132 Effect
eval_or_pass(const optional
<Policy
>& policy
,
1133 const rgw::IAM::Environment
& env
,
1134 const rgw::auth::Identity
& id
,
1138 return Effect::Pass
;
1140 return policy
->eval(env
, id
, op
, arn
);
1144 bool verify_bucket_permission(struct req_state
* const s
,
1145 const rgw_bucket
& bucket
,
1146 RGWAccessControlPolicy
* const user_acl
,
1147 RGWAccessControlPolicy
* const bucket_acl
,
1148 const optional
<Policy
>& bucket_policy
,
1151 if (!verify_requester_payer_permission(s
))
1154 auto r
= eval_or_pass(bucket_policy
, s
->env
, *s
->auth
.identity
,
1156 if (r
== Effect::Allow
)
1157 // It looks like S3 ACLs only GRANT permissions rather than
1158 // denying them, so this should be safe.
1160 else if (r
== Effect::Deny
)
1163 const auto perm
= op_to_perm(op
);
1165 return verify_bucket_permission_no_policy(s
, user_acl
, bucket_acl
, perm
);
1168 bool verify_bucket_permission_no_policy(struct req_state
* const s
,
1169 RGWAccessControlPolicy
* const user_acl
,
1170 RGWAccessControlPolicy
* const bucket_acl
,
1176 if ((perm
& (int)s
->perm_mask
) != perm
)
1179 if (bucket_acl
->verify_permission(*s
->auth
.identity
, perm
, perm
,
1180 s
->info
.env
->get("HTTP_REFERER")))
1186 return user_acl
->verify_permission(*s
->auth
.identity
, perm
, perm
);
1189 bool verify_bucket_permission_no_policy(struct req_state
* const s
, const int perm
)
1191 if (!verify_requester_payer_permission(s
))
1194 return verify_bucket_permission_no_policy(s
,
1196 s
->bucket_acl
.get(),
1200 bool verify_bucket_permission(struct req_state
* const s
, const uint64_t op
)
1202 return verify_bucket_permission(s
,
1205 s
->bucket_acl
.get(),
1210 // Authorize anyone permitted by the policy and the bucket owner
1211 // unless explicitly denied by the policy.
1213 int verify_bucket_owner_or_policy(struct req_state
* const s
,
1216 auto e
= eval_or_pass(s
->iam_policy
,
1217 s
->env
, *s
->auth
.identity
,
1218 op
, ARN(s
->bucket
));
1219 if (e
== Effect::Allow
||
1220 (e
== Effect::Pass
&&
1221 s
->auth
.identity
->is_owner_of(s
->bucket_owner
.get_id()))) {
1229 static inline bool check_deferred_bucket_perms(struct req_state
* const s
,
1230 const rgw_bucket
& bucket
,
1231 RGWAccessControlPolicy
* const user_acl
,
1232 RGWAccessControlPolicy
* const bucket_acl
,
1233 const optional
<Policy
>& bucket_policy
,
1234 const uint8_t deferred_check
,
1237 return (s
->defer_to_bucket_acls
== deferred_check \
1238 && verify_bucket_permission(s
, bucket
, user_acl
, bucket_acl
, bucket_policy
, op
));
1241 static inline bool check_deferred_bucket_only_acl(struct req_state
* const s
,
1242 RGWAccessControlPolicy
* const user_acl
,
1243 RGWAccessControlPolicy
* const bucket_acl
,
1244 const uint8_t deferred_check
,
1247 return (s
->defer_to_bucket_acls
== deferred_check \
1248 && verify_bucket_permission_no_policy(s
, user_acl
, bucket_acl
, perm
));
1251 bool verify_object_permission(struct req_state
* const s
,
1253 RGWAccessControlPolicy
* const user_acl
,
1254 RGWAccessControlPolicy
* const bucket_acl
,
1255 RGWAccessControlPolicy
* const object_acl
,
1256 const optional
<Policy
>& bucket_policy
,
1259 if (!verify_requester_payer_permission(s
))
1263 auto r
= eval_or_pass(bucket_policy
, s
->env
, *s
->auth
.identity
, op
, ARN(obj
));
1264 if (r
== Effect::Allow
)
1265 // It looks like S3 ACLs only GRANT permissions rather than
1266 // denying them, so this should be safe.
1268 else if (r
== Effect::Deny
)
1271 const auto perm
= op_to_perm(op
);
1273 if (check_deferred_bucket_perms(s
, obj
.bucket
, user_acl
, bucket_acl
, bucket_policy
,
1274 RGW_DEFER_TO_BUCKET_ACLS_RECURSE
, op
) ||
1275 check_deferred_bucket_perms(s
, obj
.bucket
, user_acl
, bucket_acl
, bucket_policy
,
1276 RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL
, rgw::IAM::s3All
)) {
1284 bool ret
= object_acl
->verify_permission(*s
->auth
.identity
, s
->perm_mask
, perm
);
1289 if (!s
->cct
->_conf
->rgw_enforce_swift_acls
)
1292 if ((perm
& (int)s
->perm_mask
) != perm
)
1296 if (perm
& (RGW_PERM_READ
| RGW_PERM_READ_ACP
))
1297 swift_perm
|= RGW_PERM_READ_OBJS
;
1298 if (perm
& RGW_PERM_WRITE
)
1299 swift_perm
|= RGW_PERM_WRITE_OBJS
;
1304 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1305 otherwise the mask might not cover the swift permissions bits */
1306 if (bucket_acl
->verify_permission(*s
->auth
.identity
, swift_perm
, swift_perm
,
1307 s
->info
.env
->get("HTTP_REFERER")))
1313 return user_acl
->verify_permission(*s
->auth
.identity
, swift_perm
, swift_perm
);
1316 bool verify_object_permission_no_policy(struct req_state
* const s
,
1317 RGWAccessControlPolicy
* const user_acl
,
1318 RGWAccessControlPolicy
* const bucket_acl
,
1319 RGWAccessControlPolicy
* const object_acl
,
1322 if (check_deferred_bucket_only_acl(s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_RECURSE
, perm
) ||
1323 check_deferred_bucket_only_acl(s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL
, RGW_PERM_FULL_CONTROL
)) {
1331 bool ret
= object_acl
->verify_permission(*s
->auth
.identity
, s
->perm_mask
, perm
);
1336 if (!s
->cct
->_conf
->rgw_enforce_swift_acls
)
1339 if ((perm
& (int)s
->perm_mask
) != perm
)
1343 if (perm
& (RGW_PERM_READ
| RGW_PERM_READ_ACP
))
1344 swift_perm
|= RGW_PERM_READ_OBJS
;
1345 if (perm
& RGW_PERM_WRITE
)
1346 swift_perm
|= RGW_PERM_WRITE_OBJS
;
1351 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1352 otherwise the mask might not cover the swift permissions bits */
1353 if (bucket_acl
->verify_permission(*s
->auth
.identity
, swift_perm
, swift_perm
,
1354 s
->info
.env
->get("HTTP_REFERER")))
1360 return user_acl
->verify_permission(*s
->auth
.identity
, swift_perm
, swift_perm
);
1363 bool verify_object_permission_no_policy(struct req_state
*s
, int perm
)
1365 if (!verify_requester_payer_permission(s
))
1368 return verify_object_permission_no_policy(s
,
1370 s
->bucket_acl
.get(),
1371 s
->object_acl
.get(),
1375 bool verify_object_permission(struct req_state
*s
, uint64_t op
)
1377 return verify_object_permission(s
,
1378 rgw_obj(s
->bucket
, s
->object
),
1380 s
->bucket_acl
.get(),
1381 s
->object_acl
.get(),
1392 memset(table
, -1, sizeof(table
));
1394 for (i
= '0'; i
<='9'; i
++)
1396 for (i
= 'A'; i
<='F'; i
++)
1397 table
[i
] = i
- 'A' + 0xa;
1398 for (i
= 'a'; i
<='f'; i
++)
1399 table
[i
] = i
- 'a' + 0xa;
1402 char to_num(char c
) {
1403 return table
[(int)c
];
1407 static char hex_to_num(char c
)
1409 static HexTable hex_table
;
1410 return hex_table
.to_num(c
);
1413 std::string
url_decode(const boost::string_view
& src_str
, bool in_query
)
1415 std::string dest_str
;
1416 dest_str
.reserve(src_str
.length() + 1);
1418 for (auto src
= std::begin(src_str
); src
!= std::end(src_str
); ++src
) {
1420 if (!in_query
|| *src
!= '+') {
1424 dest_str
.push_back(*src
);
1426 dest_str
.push_back(' ');
1429 /* 3 == strlen("%%XX") */
1430 if (std::distance(src
, std::end(src_str
)) < 3) {
1435 const char c1
= hex_to_num(*src
++);
1436 const char c2
= hex_to_num(*src
);
1437 if (c1
< 0 || c2
< 0) {
1438 return std::string();
1440 dest_str
.push_back(c1
<< 4 | c2
);
1448 void rgw_uri_escape_char(char c
, string
& dst
)
1451 snprintf(buf
, sizeof(buf
), "%%%.2X", (int)(unsigned char)c
);
1455 static bool char_needs_url_encoding(char c
)
1457 if (c
<= 0x20 || c
>= 0x7f)
1487 void url_encode(const string
& src
, string
& dst
)
1489 const char *p
= src
.c_str();
1490 for (unsigned i
= 0; i
< src
.size(); i
++, p
++) {
1491 if (char_needs_url_encoding(*p
)) {
1492 rgw_uri_escape_char(*p
, dst
);
1500 std::string
url_encode(const std::string
& src
)
1503 url_encode(src
, dst
);
1508 string
rgw_trim_whitespace(const string
& src
)
1515 for (; start
!= (int)src
.size(); start
++) {
1516 if (!isspace(src
[start
]))
1520 int end
= src
.size() - 1;
1525 for (; end
> start
; end
--) {
1526 if (!isspace(src
[end
]))
1530 return src
.substr(start
, end
- start
+ 1);
1533 boost::string_view
rgw_trim_whitespace(const boost::string_view
& src
)
1535 boost::string_view res
= src
;
1537 while (res
.size() > 0 && std::isspace(res
.front())) {
1538 res
.remove_prefix(1);
1540 while (res
.size() > 0 && std::isspace(res
.back())) {
1541 res
.remove_suffix(1);
1546 string
rgw_trim_quotes(const string
& val
)
1548 string s
= rgw_trim_whitespace(val
);
1553 int end
= s
.size() - 1;
1554 int quotes_count
= 0;
1556 if (s
[start
] == '"') {
1560 if (s
[end
] == '"') {
1564 if (quotes_count
== 2) {
1565 return s
.substr(start
, end
- start
+ 1);
1570 struct rgw_name_to_flag
{
1571 const char *type_name
;
1575 static int parse_list_of_flags(struct rgw_name_to_flag
*mapping
,
1576 const string
& str
, uint32_t *perm
)
1579 get_str_list(str
, strs
);
1580 list
<string
>::iterator iter
;
1582 for (iter
= strs
.begin(); iter
!= strs
.end(); ++iter
) {
1584 for (int i
= 0; mapping
[i
].type_name
; i
++) {
1585 if (s
.compare(mapping
[i
].type_name
) == 0)
1586 v
|= mapping
[i
].flag
;
1594 static struct rgw_name_to_flag cap_names
[] = { {"*", RGW_CAP_ALL
},
1595 {"read", RGW_CAP_READ
},
1596 {"write", RGW_CAP_WRITE
},
1599 int RGWUserCaps::parse_cap_perm(const string
& str
, uint32_t *perm
)
1601 return parse_list_of_flags(cap_names
, str
, perm
);
1604 int RGWUserCaps::get_cap(const string
& cap
, string
& type
, uint32_t *pperm
)
1606 int pos
= cap
.find('=');
1608 type
= rgw_trim_whitespace(cap
.substr(0, pos
));
1611 if (!is_valid_cap_type(type
))
1612 return -ERR_INVALID_CAP
;
1616 if (pos
< (int)cap
.size() - 1) {
1617 cap_perm
= cap
.substr(pos
+ 1);
1618 int r
= RGWUserCaps::parse_cap_perm(cap_perm
, &perm
);
1628 int RGWUserCaps::add_cap(const string
& cap
)
1633 int r
= get_cap(cap
, type
, &perm
);
1642 int RGWUserCaps::remove_cap(const string
& cap
)
1647 int r
= get_cap(cap
, type
, &perm
);
1651 map
<string
, uint32_t>::iterator iter
= caps
.find(type
);
1652 if (iter
== caps
.end())
1655 uint32_t& old_perm
= iter
->second
;
1663 int RGWUserCaps::add_from_string(const string
& str
)
1667 auto end
= str
.find(';', start
);
1668 if (end
== string::npos
)
1671 int r
= add_cap(str
.substr(start
, end
- start
));
1676 } while (start
< (int)str
.size());
1681 int RGWUserCaps::remove_from_string(const string
& str
)
1685 auto end
= str
.find(';', start
);
1686 if (end
== string::npos
)
1689 int r
= remove_cap(str
.substr(start
, end
- start
));
1694 } while (start
< (int)str
.size());
1699 void RGWUserCaps::dump(Formatter
*f
) const
1704 void RGWUserCaps::dump(Formatter
*f
, const char *name
) const
1706 f
->open_array_section(name
);
1707 map
<string
, uint32_t>::const_iterator iter
;
1708 for (iter
= caps
.begin(); iter
!= caps
.end(); ++iter
)
1710 f
->open_object_section("cap");
1711 f
->dump_string("type", iter
->first
);
1712 uint32_t perm
= iter
->second
;
1714 for (int i
=0; cap_names
[i
].type_name
; i
++) {
1715 if ((perm
& cap_names
[i
].flag
) == cap_names
[i
].flag
) {
1716 if (perm_str
.size())
1717 perm_str
.append(", ");
1719 perm_str
.append(cap_names
[i
].type_name
);
1720 perm
&= ~cap_names
[i
].flag
;
1723 if (perm_str
.empty())
1724 perm_str
= "<none>";
1726 f
->dump_string("perm", perm_str
);
1737 void decode_json(JSONObj
*obj
) {
1738 JSONDecoder::decode_json("type", type
, obj
);
1740 JSONDecoder::decode_json("perm", perm_str
, obj
);
1741 if (RGWUserCaps::parse_cap_perm(perm_str
, &perm
) < 0) {
1742 throw JSONDecoder::err("failed to parse permissions");
1747 void RGWUserCaps::decode_json(JSONObj
*obj
)
1749 list
<RGWUserCap
> caps_list
;
1750 decode_json_obj(caps_list
, obj
);
1752 list
<RGWUserCap
>::iterator iter
;
1753 for (iter
= caps_list
.begin(); iter
!= caps_list
.end(); ++iter
) {
1754 RGWUserCap
& cap
= *iter
;
1755 caps
[cap
.type
] = cap
.perm
;
1759 int RGWUserCaps::check_cap(const string
& cap
, uint32_t perm
)
1761 map
<string
, uint32_t>::iterator iter
= caps
.find(cap
);
1763 if ((iter
== caps
.end()) ||
1764 (iter
->second
& perm
) != perm
) {
1771 bool RGWUserCaps::is_valid_cap_type(const string
& tp
)
1773 static const char *cap_type
[] = { "user",
1784 for (unsigned int i
= 0; i
< sizeof(cap_type
) / sizeof(char *); ++i
) {
1785 if (tp
.compare(cap_type
[i
]) == 0) {
1793 void rgw_pool::from_str(const string
& s
)
1795 size_t pos
= rgw_unescape_str(s
, 0, '\\', ':', &name
);
1796 if (pos
!= string::npos
) {
1797 pos
= rgw_unescape_str(s
, pos
, '\\', ':', &ns
);
1798 /* ignore return; if pos != string::npos it means that we had a colon
1799 * in the middle of ns that wasn't escaped, we're going to stop there
1804 string
rgw_pool::to_str() const
1807 rgw_escape_str(name
, '\\', ':', &esc_name
);
1812 rgw_escape_str(ns
, '\\', ':', &esc_ns
);
1813 return esc_name
+ ":" + esc_ns
;
1816 void rgw_raw_obj::decode_from_rgw_obj(bufferlist::iterator
& bl
)
1819 ::decode(old_obj
, bl
);
1821 get_obj_bucket_and_oid_loc(old_obj
, oid
, loc
);
1822 pool
= old_obj
.get_explicit_data_pool();
1825 std::string
rgw_bucket::get_key(char tenant_delim
, char id_delim
) const
1827 static constexpr size_t shard_len
{12}; // ":4294967295\0"
1828 const size_t max_len
= tenant
.size() + sizeof(tenant_delim
) +
1829 name
.size() + sizeof(id_delim
) + bucket_id
.size() + shard_len
;
1832 key
.reserve(max_len
);
1833 if (!tenant
.empty() && tenant_delim
) {
1835 key
.append(1, tenant_delim
);
1838 if (!bucket_id
.empty() && id_delim
) {
1839 key
.append(1, id_delim
);
1840 key
.append(bucket_id
);
1845 std::string
rgw_bucket_shard::get_key(char tenant_delim
, char id_delim
,
1846 char shard_delim
) const
1848 auto key
= bucket
.get_key(tenant_delim
, id_delim
);
1849 if (shard_id
>= 0 && shard_delim
) {
1850 key
.append(1, shard_delim
);
1851 key
.append(std::to_string(shard_id
));
1856 static struct rgw_name_to_flag op_type_mapping
[] = { {"*", RGW_OP_TYPE_ALL
},
1857 {"read", RGW_OP_TYPE_READ
},
1858 {"write", RGW_OP_TYPE_WRITE
},
1859 {"delete", RGW_OP_TYPE_DELETE
},
1863 int rgw_parse_op_type_list(const string
& str
, uint32_t *perm
)
1865 return parse_list_of_flags(op_type_mapping
, str
, perm
);
1868 bool match_policy(boost::string_view pattern
, boost::string_view input
,
1871 const uint32_t flag2
= flag
& (MATCH_POLICY_ACTION
|MATCH_POLICY_ARN
) ?
1872 MATCH_CASE_INSENSITIVE
: 0;
1874 const auto npos
= boost::string_view::npos
;
1875 boost::string_view::size_type last_pos_input
= 0, last_pos_pattern
= 0;
1877 auto cur_pos_input
= input
.find(":", last_pos_input
);
1878 auto cur_pos_pattern
= pattern
.find(":", last_pos_pattern
);
1880 auto substr_input
= input
.substr(last_pos_input
, cur_pos_input
);
1881 auto substr_pattern
= pattern
.substr(last_pos_pattern
, cur_pos_pattern
);
1883 if (!match_wildcards(substr_pattern
, substr_input
, flag2
))
1886 if (cur_pos_pattern
== npos
)
1887 return cur_pos_input
== npos
;
1888 if (cur_pos_input
== npos
)
1891 last_pos_pattern
= cur_pos_pattern
+ 1;
1892 last_pos_input
= cur_pos_input
+ 1;