1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
8 #include <boost/tokenizer.hpp>
10 #include "json_spirit/json_spirit.h"
11 #include "common/ceph_json.h"
12 #include "common/Formatter.h"
15 #include "rgw_common.h"
17 #include "rgw_string.h"
18 #include "rgw_http_errors.h"
20 #include "rgw_data_sync.h"
22 #include "global/global_init.h"
23 #include "common/ceph_crypto.h"
24 #include "common/armor.h"
25 #include "common/errno.h"
26 #include "common/Clock.h"
27 #include "common/convenience.h"
28 #include "common/strtol.h"
29 #include "include/str_list.h"
30 #include "rgw_crypt_sanitize.h"
31 #include "rgw_bucket_sync.h"
32 #include "rgw_sync_policy.h"
34 #include "services/svc_zone.h"
38 #define dout_context g_ceph_context
40 static constexpr auto dout_subsys
= ceph_subsys_rgw
;
43 using rgw::IAM::Effect
;
44 using rgw::IAM::op_to_perm
;
45 using rgw::IAM::Policy
;
47 const uint32_t RGWBucketInfo::NUM_SHARDS_BLIND_BUCKET(UINT32_MAX
);
49 rgw_http_errors
rgw_http_s3_errors({
51 { STATUS_CREATED
, {201, "Created" }},
52 { STATUS_ACCEPTED
, {202, "Accepted" }},
53 { STATUS_NO_CONTENT
, {204, "NoContent" }},
54 { STATUS_PARTIAL_CONTENT
, {206, "" }},
55 { ERR_PERMANENT_REDIRECT
, {301, "PermanentRedirect" }},
56 { ERR_WEBSITE_REDIRECT
, {301, "WebsiteRedirect" }},
57 { STATUS_REDIRECT
, {303, "" }},
58 { ERR_NOT_MODIFIED
, {304, "NotModified" }},
59 { EINVAL
, {400, "InvalidArgument" }},
60 { ERR_INVALID_REQUEST
, {400, "InvalidRequest" }},
61 { ERR_INVALID_DIGEST
, {400, "InvalidDigest" }},
62 { ERR_BAD_DIGEST
, {400, "BadDigest" }},
63 { ERR_INVALID_LOCATION_CONSTRAINT
, {400, "InvalidLocationConstraint" }},
64 { ERR_ZONEGROUP_DEFAULT_PLACEMENT_MISCONFIGURATION
, {400, "ZonegroupDefaultPlacementMisconfiguration" }},
65 { ERR_INVALID_BUCKET_NAME
, {400, "InvalidBucketName" }},
66 { ERR_INVALID_OBJECT_NAME
, {400, "InvalidObjectName" }},
67 { ERR_UNRESOLVABLE_EMAIL
, {400, "UnresolvableGrantByEmailAddress" }},
68 { ERR_INVALID_PART
, {400, "InvalidPart" }},
69 { ERR_INVALID_PART_ORDER
, {400, "InvalidPartOrder" }},
70 { ERR_REQUEST_TIMEOUT
, {400, "RequestTimeout" }},
71 { ERR_TOO_LARGE
, {400, "EntityTooLarge" }},
72 { ERR_TOO_SMALL
, {400, "EntityTooSmall" }},
73 { ERR_TOO_MANY_BUCKETS
, {400, "TooManyBuckets" }},
74 { ERR_MALFORMED_XML
, {400, "MalformedXML" }},
75 { ERR_AMZ_CONTENT_SHA256_MISMATCH
, {400, "XAmzContentSHA256Mismatch" }},
76 { ERR_MALFORMED_DOC
, {400, "MalformedPolicyDocument"}},
77 { ERR_INVALID_TAG
, {400, "InvalidTag"}},
78 { ERR_MALFORMED_ACL_ERROR
, {400, "MalformedACLError" }},
79 { ERR_INVALID_CORS_RULES_ERROR
, {400, "InvalidRequest" }},
80 { ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR
, {400, "InvalidRequest" }},
81 { ERR_INVALID_ENCRYPTION_ALGORITHM
, {400, "InvalidEncryptionAlgorithmError" }},
82 { ERR_INVALID_RETENTION_PERIOD
,{400, "InvalidRetentionPeriod"}},
83 { ERR_LIMIT_EXCEEDED
, {400, "LimitExceeded" }},
84 { ERR_LENGTH_REQUIRED
, {411, "MissingContentLength" }},
85 { EACCES
, {403, "AccessDenied" }},
86 { EPERM
, {403, "AccessDenied" }},
87 { ERR_SIGNATURE_NO_MATCH
, {403, "SignatureDoesNotMatch" }},
88 { ERR_INVALID_ACCESS_KEY
, {403, "InvalidAccessKeyId" }},
89 { ERR_USER_SUSPENDED
, {403, "UserSuspended" }},
90 { ERR_REQUEST_TIME_SKEWED
, {403, "RequestTimeTooSkewed" }},
91 { ERR_QUOTA_EXCEEDED
, {403, "QuotaExceeded" }},
92 { ERR_MFA_REQUIRED
, {403, "AccessDenied" }},
93 { ENOENT
, {404, "NoSuchKey" }},
94 { ERR_NO_SUCH_BUCKET
, {404, "NoSuchBucket" }},
95 { ERR_NO_SUCH_WEBSITE_CONFIGURATION
, {404, "NoSuchWebsiteConfiguration" }},
96 { ERR_NO_SUCH_UPLOAD
, {404, "NoSuchUpload" }},
97 { ERR_NOT_FOUND
, {404, "Not Found"}},
98 { ERR_NO_SUCH_LC
, {404, "NoSuchLifecycleConfiguration"}},
99 { ERR_NO_SUCH_BUCKET_POLICY
, {404, "NoSuchBucketPolicy"}},
100 { ERR_NO_SUCH_USER
, {404, "NoSuchUser"}},
101 { ERR_NO_ROLE_FOUND
, {404, "NoSuchEntity"}},
102 { ERR_NO_CORS_FOUND
, {404, "NoSuchCORSConfiguration"}},
103 { ERR_NO_SUCH_SUBUSER
, {404, "NoSuchSubUser"}},
104 { ERR_NO_SUCH_ENTITY
, {404, "NoSuchEntity"}},
105 { ERR_NO_SUCH_CORS_CONFIGURATION
, {404, "NoSuchCORSConfiguration"}},
106 { ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION
, {404, "ObjectLockConfigurationNotFoundError"}},
107 { ERR_METHOD_NOT_ALLOWED
, {405, "MethodNotAllowed" }},
108 { ETIMEDOUT
, {408, "RequestTimeout" }},
109 { EEXIST
, {409, "BucketAlreadyExists" }},
110 { ERR_USER_EXIST
, {409, "UserAlreadyExists" }},
111 { ERR_EMAIL_EXIST
, {409, "EmailExists" }},
112 { ERR_KEY_EXIST
, {409, "KeyExists"}},
113 { ERR_TAG_CONFLICT
, {409, "OperationAborted"}},
114 { ERR_POSITION_NOT_EQUAL_TO_LENGTH
, {409, "PositionNotEqualToLength"}},
115 { ERR_OBJECT_NOT_APPENDABLE
, {409, "ObjectNotAppendable"}},
116 { ERR_INVALID_BUCKET_STATE
, {409, "InvalidBucketState"}},
117 { ERR_INVALID_OBJECT_STATE
, {403, "InvalidObjectState"}},
118 { ERR_INVALID_SECRET_KEY
, {400, "InvalidSecretKey"}},
119 { ERR_INVALID_KEY_TYPE
, {400, "InvalidKeyType"}},
120 { ERR_INVALID_CAP
, {400, "InvalidCapability"}},
121 { ERR_INVALID_TENANT_NAME
, {400, "InvalidTenantName" }},
122 { ENOTEMPTY
, {409, "BucketNotEmpty" }},
123 { ERR_PRECONDITION_FAILED
, {412, "PreconditionFailed" }},
124 { ERANGE
, {416, "InvalidRange" }},
125 { ERR_UNPROCESSABLE_ENTITY
, {422, "UnprocessableEntity" }},
126 { ERR_LOCKED
, {423, "Locked" }},
127 { ERR_INTERNAL_ERROR
, {500, "InternalError" }},
128 { ERR_NOT_IMPLEMENTED
, {501, "NotImplemented" }},
129 { ERR_SERVICE_UNAVAILABLE
, {503, "ServiceUnavailable"}},
130 { ERR_RATE_LIMITED
, {503, "SlowDown"}},
131 { ERR_ZERO_IN_URL
, {400, "InvalidRequest" }},
132 { ERR_NO_SUCH_TAG_SET
, {404, "NoSuchTagSet"}},
133 { ERR_NO_SUCH_BUCKET_ENCRYPTION_CONFIGURATION
, {404, "ServerSideEncryptionConfigurationNotFoundError"}},
136 rgw_http_errors
rgw_http_swift_errors({
137 { EACCES
, {403, "AccessDenied" }},
138 { EPERM
, {401, "AccessDenied" }},
139 { ENAMETOOLONG
, {400, "Metadata name too long" }},
140 { ERR_USER_SUSPENDED
, {401, "UserSuspended" }},
141 { ERR_INVALID_UTF8
, {412, "Invalid UTF8" }},
142 { ERR_BAD_URL
, {412, "Bad URL" }},
143 { ERR_NOT_SLO_MANIFEST
, {400, "Not an SLO manifest" }},
144 { ERR_QUOTA_EXCEEDED
, {413, "QuotaExceeded" }},
145 { ENOTEMPTY
, {409, "There was a conflict when trying "
146 "to complete your request." }},
147 /* FIXME(rzarzynski): we need to find a way to apply Swift's error handling
148 * procedures also for ERR_ZERO_IN_URL. This make a problem as the validation
149 * is performed very early, even before setting the req_state::proto_flags. */
150 { ERR_ZERO_IN_URL
, {412, "Invalid UTF8 or contains NULL"}},
151 { ERR_RATE_LIMITED
, {498, "Rate Limited"}},
154 rgw_http_errors
rgw_http_sts_errors({
155 { ERR_PACKED_POLICY_TOO_LARGE
, {400, "PackedPolicyTooLarge" }},
156 { ERR_INVALID_IDENTITY_TOKEN
, {400, "InvalidIdentityToken" }},
159 rgw_http_errors
rgw_http_iam_errors({
160 { EINVAL
, {400, "InvalidInput" }},
161 { ENOENT
, {404, "NoSuchEntity"}},
162 { ERR_ROLE_EXISTS
, {409, "EntityAlreadyExists"}},
163 { ERR_DELETE_CONFLICT
, {409, "DeleteConflict"}},
164 { EEXIST
, {409, "EntityAlreadyExists"}},
165 { ERR_INTERNAL_ERROR
, {500, "ServiceFailure" }},
169 using namespace ceph::crypto
;
171 thread_local
bool is_asio_thread
= false;
190 return (http_ret
== 200);
196 return !(http_ret
>= 200 && http_ret
<= 399);
199 // The requestURI transferred from the frontend can be abs_path or absoluteURI
200 // If it is absoluteURI, we should adjust it to abs_path for the following
201 // S3 authorization and some other processes depending on the requestURI
202 // The absoluteURI can start with "http://", "https://", "ws://" or "wss://"
203 static string
get_abs_path(const string
& request_uri
) {
204 const static string ABS_PREFIXS
[] = {"http://", "https://", "ws://", "wss://"};
206 for (int i
= 0; i
< 4; ++i
) {
207 if (boost::algorithm::starts_with(request_uri
, ABS_PREFIXS
[i
])) {
212 if (!isAbs
) { // it is not a valid absolute uri
215 size_t beg_pos
= request_uri
.find("://") + 3;
216 size_t len
= request_uri
.size();
217 beg_pos
= request_uri
.find('/', beg_pos
);
218 if (beg_pos
== string::npos
) return request_uri
;
219 return request_uri
.substr(beg_pos
, len
- beg_pos
);
222 req_info::req_info(CephContext
*cct
, const class RGWEnv
*env
) : env(env
) {
223 method
= env
->get("REQUEST_METHOD", "");
224 script_uri
= env
->get("SCRIPT_URI", cct
->_conf
->rgw_script_uri
.c_str());
225 request_uri
= env
->get("REQUEST_URI", cct
->_conf
->rgw_request_uri
.c_str());
226 if (request_uri
[0] != '/') {
227 request_uri
= get_abs_path(request_uri
);
229 auto pos
= request_uri
.find('?');
230 if (pos
!= string::npos
) {
231 request_params
= request_uri
.substr(pos
+ 1);
232 request_uri
= request_uri
.substr(0, pos
);
234 request_params
= env
->get("QUERY_STRING", "");
236 host
= env
->get("HTTP_HOST", "");
238 // strip off any trailing :port from host (added by CrossFTP and maybe others)
239 size_t colon_offset
= host
.find_last_of(':');
240 if (colon_offset
!= string::npos
) {
241 bool all_digits
= true;
242 for (unsigned i
= colon_offset
+ 1; i
< host
.size(); ++i
) {
243 if (!isdigit(host
[i
])) {
249 host
.resize(colon_offset
);
254 void req_info::rebuild_from(req_info
& src
)
257 script_uri
= src
.script_uri
;
259 if (src
.effective_uri
.empty()) {
260 request_uri
= src
.request_uri
;
262 request_uri
= src
.effective_uri
;
264 effective_uri
.clear();
267 x_meta_map
= src
.x_meta_map
;
268 x_meta_map
.erase("x-amz-date");
272 req_state::req_state(CephContext
* _cct
, const RGWProcessEnv
& penv
,
273 RGWEnv
* e
, uint64_t id
)
274 : cct(_cct
), penv(penv
), info(_cct
, e
), id(id
)
276 enable_ops_log
= e
->get_enable_ops_log();
277 enable_usage_log
= e
->get_enable_usage_log();
278 defer_to_bucket_acls
= e
->get_defer_to_bucket_acls();
283 req_state::~req_state() {
287 std::ostream
& req_state::gen_prefix(std::ostream
& out
) const
289 auto p
= out
.precision();
290 return out
<< "req " << id
<< ' '
291 << std::setprecision(3) << std::fixed
<< time_elapsed() // '0.123s'
292 << std::setprecision(p
) << std::defaultfloat
<< ' ';
295 bool search_err(rgw_http_errors
& errs
, int err_no
, int& http_ret
, string
& code
)
297 auto r
= errs
.find(err_no
);
298 if (r
!= errs
.end()) {
299 http_ret
= r
->second
.first
;
300 code
= r
->second
.second
;
306 void set_req_state_err(struct rgw_err
& err
, /* out */
308 const int prot_flags
) /* in */
315 if (prot_flags
& RGW_REST_SWIFT
) {
316 if (search_err(rgw_http_swift_errors
, err_no
, err
.http_ret
, err
.err_code
))
320 if (prot_flags
& RGW_REST_STS
) {
321 if (search_err(rgw_http_sts_errors
, err_no
, err
.http_ret
, err
.err_code
))
325 if (prot_flags
& RGW_REST_IAM
) {
326 if (search_err(rgw_http_iam_errors
, err_no
, err
.http_ret
, err
.err_code
))
330 //Default to searching in s3 errors
331 if (search_err(rgw_http_s3_errors
, err_no
, err
.http_ret
, err
.err_code
))
333 dout(0) << "WARNING: set_req_state_err err_no=" << err_no
334 << " resorting to 500" << dendl
;
337 err
.err_code
= "UnknownError";
340 void set_req_state_err(req_state
* s
, int err_no
, const string
& err_msg
)
343 set_req_state_err(s
, err_no
);
344 if (s
->prot_flags
& RGW_REST_SWIFT
&& !err_msg
.empty()) {
345 /* TODO(rzarzynski): there never ever should be a check like this one.
346 * It's here only for the sake of the patch's backportability. Further
347 * commits will move the logic to a per-RGWHandler replacement of
348 * the end_header() function. Alternativaly, we might consider making
349 * that just for the dump(). Please take a look on @cbodley's comments
350 * in PR #10690 (https://github.com/ceph/ceph/pull/10690). */
351 s
->err
.err_code
= err_msg
;
353 s
->err
.message
= err_msg
;
358 void set_req_state_err(req_state
* s
, int err_no
)
361 set_req_state_err(s
->err
, err_no
, s
->prot_flags
);
365 void dump(req_state
* s
)
367 if (s
->format
!= RGWFormat::HTML
)
368 s
->formatter
->open_object_section("Error");
369 if (!s
->err
.err_code
.empty())
370 s
->formatter
->dump_string("Code", s
->err
.err_code
);
371 s
->formatter
->dump_string("Message", s
->err
.message
);
372 if (!s
->bucket_name
.empty()) // TODO: connect to expose_bucket
373 s
->formatter
->dump_string("BucketName", s
->bucket_name
);
374 if (!s
->trans_id
.empty()) // TODO: connect to expose_bucket or another toggle
375 s
->formatter
->dump_string("RequestId", s
->trans_id
);
376 s
->formatter
->dump_string("HostId", s
->host_id
);
377 if (s
->format
!= RGWFormat::HTML
)
378 s
->formatter
->close_section();
386 #define STR_LEN_ENTRY(s) { s, sizeof(s) - 1 }
388 struct str_len meta_prefixes
[] = { STR_LEN_ENTRY("HTTP_X_AMZ_"),
389 STR_LEN_ENTRY("HTTP_X_GOOG_"),
390 STR_LEN_ENTRY("HTTP_X_DHO_"),
391 STR_LEN_ENTRY("HTTP_X_RGW_"),
392 STR_LEN_ENTRY("HTTP_X_OBJECT_"),
393 STR_LEN_ENTRY("HTTP_X_CONTAINER_"),
394 STR_LEN_ENTRY("HTTP_X_ACCOUNT_"),
397 void req_info::init_meta_info(const DoutPrefixProvider
*dpp
, bool *found_bad_meta
)
400 crypt_attribute_map
.clear();
402 for (const auto& kv
: env
->get_map()) {
404 const string
& header_name
= kv
.first
;
405 const string
& val
= kv
.second
;
406 for (int prefix_num
= 0; (prefix
= meta_prefixes
[prefix_num
].str
) != NULL
; prefix_num
++) {
407 int len
= meta_prefixes
[prefix_num
].len
;
408 const char *p
= header_name
.c_str();
409 if (strncmp(p
, prefix
, len
) == 0) {
410 ldpp_dout(dpp
, 10) << "meta>> " << p
<< dendl
;
411 const char *name
= p
+len
; /* skip the prefix */
412 int name_len
= header_name
.size() - len
;
414 if (found_bad_meta
&& strncmp(name
, "META_", name_len
) == 0)
415 *found_bad_meta
= true;
417 char name_low
[meta_prefixes
[0].len
+ name_len
+ 1];
418 snprintf(name_low
, meta_prefixes
[0].len
- 5 + name_len
+ 1, "%s%s", meta_prefixes
[0].str
+ 5 /* skip HTTP_ */, name
); // normalize meta prefix
420 for (j
= 0; name_low
[j
]; j
++) {
421 if (name_low
[j
] == '_')
423 else if (name_low
[j
] == '-')
426 name_low
[j
] = tolower(name_low
[j
]);
430 auto it
= x_meta_map
.find(name_low
);
431 if (it
!= x_meta_map
.end()) {
432 string old
= it
->second
;
433 boost::algorithm::trim_right(old
);
436 x_meta_map
[name_low
] = old
;
438 x_meta_map
[name_low
] = val
;
440 if (strncmp(name_low
, "x-amz-server-side-encryption", 20) == 0) {
441 crypt_attribute_map
[name_low
] = val
;
446 for (const auto& kv
: x_meta_map
) {
447 ldpp_dout(dpp
, 10) << "x>> " << kv
.first
<< ":" << rgw::crypt_sanitize::x_meta_map
{kv
.first
, kv
.second
} << dendl
;
451 std::ostream
& operator<<(std::ostream
& oss
, const rgw_err
&err
)
453 oss
<< "rgw_err(http_ret=" << err
.http_ret
<< ", err_code='" << err
.err_code
<< "') ";
457 void rgw_add_amz_meta_header(
458 meta_map_t
& x_meta_map
,
459 const std::string
& k
,
460 const std::string
& v
)
462 auto it
= x_meta_map
.find(k
);
463 if (it
!= x_meta_map
.end()) {
464 std::string old
= it
->second
;
465 boost::algorithm::trim_right(old
);
474 bool rgw_set_amz_meta_header(
475 meta_map_t
& x_meta_map
,
476 const std::string
& k
,
477 const std::string
& v
,
478 rgw_set_action_if_set a
)
480 auto it
{ x_meta_map
.find(k
) };
481 bool r
{ it
!= x_meta_map
.end() };
489 std::string old
{ it
->second
};
490 boost::algorithm::trim_right(old
);
503 string
rgw_string_unquote(const string
& s
)
505 if (s
[0] != '"' || s
.size() < 2)
509 for (len
= s
.size(); len
> 2; --len
) {
510 if (s
[len
- 1] != ' ')
517 return s
.substr(1, len
- 2);
520 static bool check_str_end(const char *s
)
533 static bool check_gmt_end(const char *s
)
538 while (isspace(*s
)) {
542 /* check for correct timezone */
543 if ((strncmp(s
, "GMT", 3) != 0) &&
544 (strncmp(s
, "UTC", 3) != 0)) {
551 static bool parse_rfc850(const char *s
, struct tm
*t
)
553 // FIPS zeroization audit 20191115: this memset is not security related.
554 memset(t
, 0, sizeof(*t
));
555 return check_gmt_end(strptime(s
, "%A, %d-%b-%y %H:%M:%S ", t
));
558 static bool parse_asctime(const char *s
, struct tm
*t
)
560 // FIPS zeroization audit 20191115: this memset is not security related.
561 memset(t
, 0, sizeof(*t
));
562 return check_str_end(strptime(s
, "%a %b %d %H:%M:%S %Y", t
));
565 static bool parse_rfc1123(const char *s
, struct tm
*t
)
567 // FIPS zeroization audit 20191115: this memset is not security related.
568 memset(t
, 0, sizeof(*t
));
569 return check_gmt_end(strptime(s
, "%a, %d %b %Y %H:%M:%S ", t
));
572 static bool parse_rfc1123_alt(const char *s
, struct tm
*t
)
574 // FIPS zeroization audit 20191115: this memset is not security related.
575 memset(t
, 0, sizeof(*t
));
576 return check_str_end(strptime(s
, "%a, %d %b %Y %H:%M:%S %z", t
));
579 bool parse_rfc2616(const char *s
, struct tm
*t
)
581 return parse_rfc850(s
, t
) || parse_asctime(s
, t
) || parse_rfc1123(s
, t
) || parse_rfc1123_alt(s
,t
);
584 bool parse_iso8601(const char *s
, struct tm
*t
, uint32_t *pns
, bool extended_format
)
586 // FIPS zeroization audit 20191115: this memset is not security related.
587 memset(t
, 0, sizeof(*t
));
593 if (extended_format
) {
594 p
= strptime(s
, "%Y-%m-%dT%T", t
);
596 p
= strptime(s
, "%Y-%m-%d %T", t
);
599 p
= strptime(s
, "%Y%m%dT%H%M%S", t
);
602 dout(0) << "parse_iso8601 failed" << dendl
;
605 const std::string_view str
= rgw_trim_whitespace(std::string_view(p
));
606 int len
= str
.size();
608 if (len
== 0 || (len
== 1 && str
[0] == 'Z'))
616 std::string_view nsstr
= str
.substr(1, len
- 2);
617 int r
= stringtoul(std::string(nsstr
), &ms
);
625 if (nsstr
.size() > 9) {
626 nsstr
= nsstr
.substr(0, 9);
629 uint64_t mul_table
[] = { 0,
641 *pns
= ms
* mul_table
[nsstr
.size()];
646 int parse_key_value(string
& in_str
, const char *delim
, string
& key
, string
& val
)
651 auto pos
= in_str
.find(delim
);
652 if (pos
== string::npos
)
655 key
= rgw_trim_whitespace(in_str
.substr(0, pos
));
656 val
= rgw_trim_whitespace(in_str
.substr(pos
+ 1));
661 int parse_key_value(string
& in_str
, string
& key
, string
& val
)
663 return parse_key_value(in_str
, "=", key
,val
);
666 boost::optional
<std::pair
<std::string_view
, std::string_view
>>
667 parse_key_value(const std::string_view
& in_str
,
668 const std::string_view
& delim
)
670 const size_t pos
= in_str
.find(delim
);
671 if (pos
== std::string_view::npos
) {
675 const auto key
= rgw_trim_whitespace(in_str
.substr(0, pos
));
676 const auto val
= rgw_trim_whitespace(in_str
.substr(pos
+ 1));
678 return std::make_pair(key
, val
);
681 boost::optional
<std::pair
<std::string_view
, std::string_view
>>
682 parse_key_value(const std::string_view
& in_str
)
684 return parse_key_value(in_str
, "=");
687 int parse_time(const char *time_str
, real_time
*time
)
692 if (!parse_rfc2616(time_str
, &tm
) && !parse_iso8601(time_str
, &tm
, &ns
)) {
696 time_t sec
= internal_timegm(&tm
);
697 *time
= utime_t(sec
, ns
).to_real_time();
702 #define TIME_BUF_SIZE 128
704 void rgw_to_iso8601(const real_time
& t
, char *dest
, int buf_size
)
708 char buf
[TIME_BUF_SIZE
];
710 time_t epoch
= ut
.sec();
711 struct tm
*tmp
= gmtime_r(&epoch
, &result
);
715 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T", tmp
) == 0)
718 snprintf(dest
, buf_size
, "%s.%03dZ", buf
, (int)(ut
.usec() / 1000));
721 void rgw_to_iso8601(const real_time
& t
, string
*dest
)
723 char buf
[TIME_BUF_SIZE
];
724 rgw_to_iso8601(t
, buf
, sizeof(buf
));
729 string
rgw_to_asctime(const utime_t
& t
)
737 * calculate the sha1 value of a given msg and key
739 void calc_hmac_sha1(const char *key
, int key_len
,
740 const char *msg
, int msg_len
, char *dest
)
741 /* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
743 HMACSHA1
hmac((const unsigned char *)key
, key_len
);
744 hmac
.Update((const unsigned char *)msg
, msg_len
);
745 hmac
.Final((unsigned char *)dest
);
749 * calculate the sha256 value of a given msg and key
751 void calc_hmac_sha256(const char *key
, int key_len
,
752 const char *msg
, int msg_len
, char *dest
)
754 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
756 HMACSHA256
hmac((const unsigned char *)key
, key_len
);
757 hmac
.Update((const unsigned char *)msg
, msg_len
);
758 hmac
.Final((unsigned char *)hash_sha256
);
760 memcpy(dest
, hash_sha256
, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
);
763 using ceph::crypto::SHA256
;
766 * calculate the sha256 hash value of a given msg
768 sha256_digest_t
calc_hash_sha256(const std::string_view
& msg
)
770 sha256_digest_t hash
;
773 hasher
.Update(reinterpret_cast<const unsigned char*>(msg
.data()), msg
.size());
774 hasher
.Final(hash
.v
);
779 SHA256
* calc_hash_sha256_open_stream()
784 void calc_hash_sha256_update_stream(SHA256
*hash
, const char *msg
, int len
)
786 hash
->Update((const unsigned char *)msg
, len
);
789 string
calc_hash_sha256_close_stream(SHA256
**phash
)
791 SHA256
*hash
= *phash
;
793 hash
= calc_hash_sha256_open_stream();
795 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
797 hash
->Final((unsigned char *)hash_sha256
);
799 char hex_str
[(CEPH_CRYPTO_SHA256_DIGESTSIZE
* 2) + 1];
800 buf_to_hex((unsigned char *)hash_sha256
, CEPH_CRYPTO_SHA256_DIGESTSIZE
, hex_str
);
805 return std::string(hex_str
);
808 std::string
calc_hash_sha256_restart_stream(SHA256
**phash
)
810 const auto hash
= calc_hash_sha256_close_stream(phash
);
811 *phash
= calc_hash_sha256_open_stream();
818 auto delim_pos
= str
.find('=');
821 if (delim_pos
== string::npos
) {
826 name
= str
.substr(0, delim_pos
);
827 val
= str
.substr(delim_pos
+ 1);
833 int RGWHTTPArgs::parse(const DoutPrefixProvider
*dpp
)
845 int fpos
= str
.find('&', pos
);
850 std::string nameval
= url_decode(str
.substr(pos
, fpos
- pos
), true);
851 NameVal
nv(std::move(nameval
));
852 int ret
= nv
.parse();
854 string
& name
= nv
.get_name();
855 if (name
.find("X-Amz-") != string::npos
) {
856 std::for_each(name
.begin(),
860 c
= ::tolower(static_cast<unsigned char>(c
));
864 string
& val
= nv
.get_val();
865 ldpp_dout(dpp
, 10) << "name: " << name
<< " val: " << val
<< dendl
;
875 void RGWHTTPArgs::remove(const string
& name
)
877 auto val_iter
= val_map
.find(name
);
878 if (val_iter
!= std::end(val_map
)) {
879 val_map
.erase(val_iter
);
882 auto sys_val_iter
= sys_val_map
.find(name
);
883 if (sys_val_iter
!= std::end(sys_val_map
)) {
884 sys_val_map
.erase(sys_val_iter
);
887 auto subres_iter
= sub_resources
.find(name
);
888 if (subres_iter
!= std::end(sub_resources
)) {
889 sub_resources
.erase(subres_iter
);
893 void RGWHTTPArgs::append(const string
& name
, const string
& val
)
895 if (name
.compare(0, sizeof(RGW_SYS_PARAM_PREFIX
) - 1, RGW_SYS_PARAM_PREFIX
) == 0) {
896 sys_val_map
[name
] = val
;
901 // when sub_resources exclusive by object are added, please remember to update obj_sub_resource in RGWHTTPArgs::exist_obj_excl_sub_resource().
902 if ((name
.compare("acl") == 0) ||
903 (name
.compare("cors") == 0) ||
904 (name
.compare("notification") == 0) ||
905 (name
.compare("location") == 0) ||
906 (name
.compare("logging") == 0) ||
907 (name
.compare("usage") == 0) ||
908 (name
.compare("lifecycle") == 0) ||
909 (name
.compare("delete") == 0) ||
910 (name
.compare("uploads") == 0) ||
911 (name
.compare("partNumber") == 0) ||
912 (name
.compare("uploadId") == 0) ||
913 (name
.compare("versionId") == 0) ||
914 (name
.compare("start-date") == 0) ||
915 (name
.compare("end-date") == 0) ||
916 (name
.compare("versions") == 0) ||
917 (name
.compare("versioning") == 0) ||
918 (name
.compare("website") == 0) ||
919 (name
.compare("requestPayment") == 0) ||
920 (name
.compare("torrent") == 0) ||
921 (name
.compare("tagging") == 0) ||
922 (name
.compare("append") == 0) ||
923 (name
.compare("position") == 0) ||
924 (name
.compare("policyStatus") == 0) ||
925 (name
.compare("publicAccessBlock") == 0)) {
926 sub_resources
[name
] = val
;
927 } else if (name
[0] == 'r') { // root of all evil
928 if ((name
.compare("response-content-type") == 0) ||
929 (name
.compare("response-content-language") == 0) ||
930 (name
.compare("response-expires") == 0) ||
931 (name
.compare("response-cache-control") == 0) ||
932 (name
.compare("response-content-disposition") == 0) ||
933 (name
.compare("response-content-encoding") == 0)) {
934 sub_resources
[name
] = val
;
935 has_resp_modifier
= true;
937 } else if ((name
.compare("subuser") == 0) ||
938 (name
.compare("key") == 0) ||
939 (name
.compare("caps") == 0) ||
940 (name
.compare("index") == 0) ||
941 (name
.compare("policy") == 0) ||
942 (name
.compare("quota") == 0) ||
943 (name
.compare("list") == 0) ||
944 (name
.compare("object") == 0) ||
945 (name
.compare("sync") == 0)) {
946 if (!admin_subresource_added
) {
947 sub_resources
[name
] = "";
948 admin_subresource_added
= true;
953 const string
& RGWHTTPArgs::get(const string
& name
, bool *exists
) const
955 auto iter
= val_map
.find(name
);
956 bool e
= (iter
!= std::end(val_map
));
964 boost::optional
<const std::string
&>
965 RGWHTTPArgs::get_optional(const std::string
& name
) const
968 const std::string
& value
= get(name
, &exists
);
976 int RGWHTTPArgs::get_bool(const string
& name
, bool *val
, bool *exists
) const
978 map
<string
, string
>::const_iterator iter
;
979 iter
= val_map
.find(name
);
980 bool e
= (iter
!= val_map
.end());
985 const char *s
= iter
->second
.c_str();
987 if (strcasecmp(s
, "false") == 0) {
989 } else if (strcasecmp(s
, "true") == 0) {
999 int RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool *exists
) const
1002 return get_bool(s
, val
, exists
);
1005 void RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool def_val
) const
1007 bool exists
= false;
1008 if ((get_bool(name
, val
, &exists
) < 0) ||
1014 int RGWHTTPArgs::get_int(const char *name
, int *val
, int def_val
) const
1016 bool exists
= false;
1018 val_str
= get(name
, &exists
);
1026 *val
= (int)strict_strtol(val_str
.c_str(), 10, &err
);
1034 string
RGWHTTPArgs::sys_get(const string
& name
, bool * const exists
) const
1036 const auto iter
= sys_val_map
.find(name
);
1037 const bool e
= (iter
!= sys_val_map
.end());
1043 return e
? iter
->second
: string();
1046 bool rgw_transport_is_secure(CephContext
*cct
, const RGWEnv
& env
)
1048 const auto& m
= env
.get_map();
1049 // frontend connected with ssl
1050 if (m
.count("SERVER_PORT_SECURE")) {
1053 // ignore proxy headers unless explicitly enabled
1054 if (!cct
->_conf
->rgw_trust_forwarded_https
) {
1057 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
1058 // Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>
1059 auto i
= m
.find("HTTP_FORWARDED");
1060 if (i
!= m
.end() && i
->second
.find("proto=https") != std::string::npos
) {
1063 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
1064 i
= m
.find("HTTP_X_FORWARDED_PROTO");
1065 if (i
!= m
.end() && i
->second
== "https") {
1074 struct perm_state_from_req_state
: public perm_state_base
{
1075 req_state
* const s
;
1076 perm_state_from_req_state(req_state
* const _s
)
1077 : perm_state_base(_s
->cct
,
1079 _s
->auth
.identity
.get(),
1080 _s
->bucket
.get() ? _s
->bucket
->get_info() : RGWBucketInfo(),
1082 _s
->defer_to_bucket_acls
,
1083 _s
->bucket_access_conf
),
1086 std::optional
<bool> get_request_payer() const override
{
1087 const char *request_payer
= s
->info
.env
->get("HTTP_X_AMZ_REQUEST_PAYER");
1088 if (!request_payer
) {
1090 request_payer
= s
->info
.args
.get("x-amz-request-payer", &exists
).c_str();
1096 if (strcasecmp(request_payer
, "requester") == 0) {
1100 return std::nullopt
;
1103 const char *get_referer() const override
{
1104 return s
->info
.env
->get("HTTP_REFERER");
1108 Effect
eval_or_pass(const DoutPrefixProvider
* dpp
,
1109 const boost::optional
<Policy
>& policy
,
1110 const rgw::IAM::Environment
& env
,
1111 boost::optional
<const rgw::auth::Identity
&> id
,
1113 const ARN
& resource
,
1114 boost::optional
<rgw::IAM::PolicyPrincipal
&> princ_type
=boost::none
) {
1116 return Effect::Pass
;
1118 return policy
->eval(env
, id
, op
, resource
, princ_type
);
1123 Effect
eval_identity_or_session_policies(const DoutPrefixProvider
* dpp
,
1124 const vector
<Policy
>& policies
,
1125 const rgw::IAM::Environment
& env
,
1128 auto policy_res
= Effect::Pass
, prev_res
= Effect::Pass
;
1129 for (auto& policy
: policies
) {
1130 if (policy_res
= eval_or_pass(dpp
, policy
, env
, boost::none
, op
, arn
); policy_res
== Effect::Deny
)
1132 else if (policy_res
== Effect::Allow
)
1133 prev_res
= Effect::Allow
;
1134 else if (policy_res
== Effect::Pass
&& prev_res
== Effect::Allow
)
1135 policy_res
= Effect::Allow
;
1140 bool verify_user_permission(const DoutPrefixProvider
* dpp
,
1141 perm_state_base
* const s
,
1142 RGWAccessControlPolicy
* const user_acl
,
1143 const vector
<rgw::IAM::Policy
>& user_policies
,
1144 const vector
<rgw::IAM::Policy
>& session_policies
,
1145 const rgw::ARN
& res
,
1147 bool mandatory_policy
)
1149 auto identity_policy_res
= eval_identity_or_session_policies(dpp
, user_policies
, s
->env
, op
, res
);
1150 if (identity_policy_res
== Effect::Deny
) {
1154 if (! session_policies
.empty()) {
1155 auto session_policy_res
= eval_identity_or_session_policies(dpp
, session_policies
, s
->env
, op
, res
);
1156 if (session_policy_res
== Effect::Deny
) {
1159 //Intersection of identity policies and session policies
1160 if (identity_policy_res
== Effect::Allow
&& session_policy_res
== Effect::Allow
) {
1166 if (identity_policy_res
== Effect::Allow
) {
1170 if (mandatory_policy
) {
1171 // no policies, and policy is mandatory
1172 ldpp_dout(dpp
, 20) << "no policies for a policy mandatory op " << op
<< dendl
;
1176 auto perm
= op_to_perm(op
);
1178 return verify_user_permission_no_policy(dpp
, s
, user_acl
, perm
);
1181 bool verify_user_permission_no_policy(const DoutPrefixProvider
* dpp
,
1182 struct perm_state_base
* const s
,
1183 RGWAccessControlPolicy
* const user_acl
,
1186 if (s
->identity
->get_identity_type() == TYPE_ROLE
)
1189 /* S3 doesn't support account ACLs. */
1193 if ((perm
& (int)s
->perm_mask
) != perm
)
1196 return user_acl
->verify_permission(dpp
, *s
->identity
, perm
, perm
);
1199 bool verify_user_permission(const DoutPrefixProvider
* dpp
,
1200 req_state
* const s
,
1201 const rgw::ARN
& res
,
1203 bool mandatory_policy
)
1205 perm_state_from_req_state
ps(s
);
1206 return verify_user_permission(dpp
, &ps
, s
->user_acl
.get(), s
->iam_user_policies
, s
->session_policies
, res
, op
, mandatory_policy
);
1209 bool verify_user_permission_no_policy(const DoutPrefixProvider
* dpp
,
1210 req_state
* const s
,
1213 perm_state_from_req_state
ps(s
);
1214 return verify_user_permission_no_policy(dpp
, &ps
, s
->user_acl
.get(), perm
);
1217 bool verify_requester_payer_permission(struct perm_state_base
*s
)
1219 if (!s
->bucket_info
.requester_pays
)
1222 if (s
->identity
->is_owner_of(s
->bucket_info
.owner
))
1225 if (s
->identity
->is_anonymous()) {
1229 auto request_payer
= s
->get_request_payer();
1230 if (request_payer
) {
1231 return *request_payer
;
1237 bool verify_bucket_permission(const DoutPrefixProvider
* dpp
,
1238 struct perm_state_base
* const s
,
1239 const rgw_bucket
& bucket
,
1240 RGWAccessControlPolicy
* const user_acl
,
1241 RGWAccessControlPolicy
* const bucket_acl
,
1242 const boost::optional
<Policy
>& bucket_policy
,
1243 const vector
<Policy
>& identity_policies
,
1244 const vector
<Policy
>& session_policies
,
1247 if (!verify_requester_payer_permission(s
))
1250 auto identity_policy_res
= eval_identity_or_session_policies(dpp
, identity_policies
, s
->env
, op
, ARN(bucket
));
1251 if (identity_policy_res
== Effect::Deny
)
1254 rgw::IAM::PolicyPrincipal princ_type
= rgw::IAM::PolicyPrincipal::Other
;
1255 if (bucket_policy
) {
1256 ldpp_dout(dpp
, 16) << __func__
<< ": policy: " << bucket_policy
.get()
1257 << "resource: " << ARN(bucket
) << dendl
;
1259 auto r
= eval_or_pass(dpp
, bucket_policy
, s
->env
, *s
->identity
,
1260 op
, ARN(bucket
), princ_type
);
1261 if (r
== Effect::Deny
)
1264 //Take into account session policies, if the identity making a request is a role
1265 if (!session_policies
.empty()) {
1266 auto session_policy_res
= eval_identity_or_session_policies(dpp
, session_policies
, s
->env
, op
, ARN(bucket
));
1267 if (session_policy_res
== Effect::Deny
) {
1270 if (princ_type
== rgw::IAM::PolicyPrincipal::Role
) {
1271 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1272 if ((session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
) ||
1273 (session_policy_res
== Effect::Allow
&& r
== Effect::Allow
))
1275 } else if (princ_type
== rgw::IAM::PolicyPrincipal::Session
) {
1276 //Intersection of session policy and identity policy plus bucket policy
1277 if ((session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
) || r
== Effect::Allow
)
1279 } else if (princ_type
== rgw::IAM::PolicyPrincipal::Other
) {// there was no match in the bucket policy
1280 if (session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
)
1286 if (r
== Effect::Allow
|| identity_policy_res
== Effect::Allow
)
1287 // It looks like S3 ACLs only GRANT permissions rather than
1288 // denying them, so this should be safe.
1291 const auto perm
= op_to_perm(op
);
1293 return verify_bucket_permission_no_policy(dpp
, s
, user_acl
, bucket_acl
, perm
);
1296 bool verify_bucket_permission(const DoutPrefixProvider
* dpp
,
1297 req_state
* const s
,
1298 const rgw_bucket
& bucket
,
1299 RGWAccessControlPolicy
* const user_acl
,
1300 RGWAccessControlPolicy
* const bucket_acl
,
1301 const boost::optional
<Policy
>& bucket_policy
,
1302 const vector
<Policy
>& user_policies
,
1303 const vector
<Policy
>& session_policies
,
1306 perm_state_from_req_state
ps(s
);
1307 return verify_bucket_permission(dpp
, &ps
, bucket
,
1308 user_acl
, bucket_acl
,
1309 bucket_policy
, user_policies
,
1310 session_policies
, op
);
1313 bool verify_bucket_permission_no_policy(const DoutPrefixProvider
* dpp
, struct perm_state_base
* const s
,
1314 RGWAccessControlPolicy
* const user_acl
,
1315 RGWAccessControlPolicy
* const bucket_acl
,
1321 if ((perm
& (int)s
->perm_mask
) != perm
)
1324 if (bucket_acl
->verify_permission(dpp
, *s
->identity
, perm
, perm
,
1326 s
->bucket_access_conf
&&
1327 s
->bucket_access_conf
->ignore_public_acls()))
1333 return user_acl
->verify_permission(dpp
, *s
->identity
, perm
, perm
);
1336 bool verify_bucket_permission_no_policy(const DoutPrefixProvider
* dpp
, req_state
* const s
,
1337 RGWAccessControlPolicy
* const user_acl
,
1338 RGWAccessControlPolicy
* const bucket_acl
,
1341 perm_state_from_req_state
ps(s
);
1342 return verify_bucket_permission_no_policy(dpp
,
1349 bool verify_bucket_permission_no_policy(const DoutPrefixProvider
* dpp
, req_state
* const s
, const int perm
)
1351 perm_state_from_req_state
ps(s
);
1353 if (!verify_requester_payer_permission(&ps
))
1356 return verify_bucket_permission_no_policy(dpp
,
1359 s
->bucket_acl
.get(),
1363 bool verify_bucket_permission(const DoutPrefixProvider
* dpp
, req_state
* const s
, const uint64_t op
)
1365 if (rgw::sal::Bucket::empty(s
->bucket
)) {
1366 // request is missing a bucket name
1370 perm_state_from_req_state
ps(s
);
1372 return verify_bucket_permission(dpp
,
1374 s
->bucket
->get_key(),
1376 s
->bucket_acl
.get(),
1378 s
->iam_user_policies
,
1379 s
->session_policies
,
1383 // Authorize anyone permitted by the bucket policy, identity policies, session policies and the bucket owner
1384 // unless explicitly denied by the policy.
1386 int verify_bucket_owner_or_policy(req_state
* const s
,
1389 auto identity_policy_res
= eval_identity_or_session_policies(s
, s
->iam_user_policies
, s
->env
, op
, ARN(s
->bucket
->get_key()));
1390 if (identity_policy_res
== Effect::Deny
) {
1394 rgw::IAM::PolicyPrincipal princ_type
= rgw::IAM::PolicyPrincipal::Other
;
1395 auto e
= eval_or_pass(s
, s
->iam_policy
,
1396 s
->env
, *s
->auth
.identity
,
1397 op
, ARN(s
->bucket
->get_key()), princ_type
);
1398 if (e
== Effect::Deny
) {
1402 if (!s
->session_policies
.empty()) {
1403 auto session_policy_res
= eval_identity_or_session_policies(s
, s
->session_policies
, s
->env
, op
,
1404 ARN(s
->bucket
->get_key()));
1405 if (session_policy_res
== Effect::Deny
) {
1408 if (princ_type
== rgw::IAM::PolicyPrincipal::Role
) {
1409 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1410 if ((session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
) ||
1411 (session_policy_res
== Effect::Allow
&& e
== Effect::Allow
))
1413 } else if (princ_type
== rgw::IAM::PolicyPrincipal::Session
) {
1414 //Intersection of session policy and identity policy plus bucket policy
1415 if ((session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
) || e
== Effect::Allow
)
1417 } else if (princ_type
== rgw::IAM::PolicyPrincipal::Other
) {// there was no match in the bucket policy
1418 if (session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
)
1424 if (e
== Effect::Allow
||
1425 identity_policy_res
== Effect::Allow
||
1426 (e
== Effect::Pass
&&
1427 identity_policy_res
== Effect::Pass
&&
1428 s
->auth
.identity
->is_owner_of(s
->bucket_owner
.get_id()))) {
1436 static inline bool check_deferred_bucket_perms(const DoutPrefixProvider
* dpp
,
1437 struct perm_state_base
* const s
,
1438 const rgw_bucket
& bucket
,
1439 RGWAccessControlPolicy
* const user_acl
,
1440 RGWAccessControlPolicy
* const bucket_acl
,
1441 const boost::optional
<Policy
>& bucket_policy
,
1442 const vector
<Policy
>& identity_policies
,
1443 const vector
<Policy
>& session_policies
,
1444 const uint8_t deferred_check
,
1447 return (s
->defer_to_bucket_acls
== deferred_check \
1448 && verify_bucket_permission(dpp
, s
, bucket
, user_acl
, bucket_acl
, bucket_policy
, identity_policies
, session_policies
,op
));
1451 static inline bool check_deferred_bucket_only_acl(const DoutPrefixProvider
* dpp
,
1452 struct perm_state_base
* const s
,
1453 RGWAccessControlPolicy
* const user_acl
,
1454 RGWAccessControlPolicy
* const bucket_acl
,
1455 const uint8_t deferred_check
,
1458 return (s
->defer_to_bucket_acls
== deferred_check \
1459 && verify_bucket_permission_no_policy(dpp
, s
, user_acl
, bucket_acl
, perm
));
1462 bool verify_object_permission(const DoutPrefixProvider
* dpp
, struct perm_state_base
* const s
,
1464 RGWAccessControlPolicy
* const user_acl
,
1465 RGWAccessControlPolicy
* const bucket_acl
,
1466 RGWAccessControlPolicy
* const object_acl
,
1467 const boost::optional
<Policy
>& bucket_policy
,
1468 const vector
<Policy
>& identity_policies
,
1469 const vector
<Policy
>& session_policies
,
1472 if (!verify_requester_payer_permission(s
))
1475 auto identity_policy_res
= eval_identity_or_session_policies(dpp
, identity_policies
, s
->env
, op
, ARN(obj
));
1476 if (identity_policy_res
== Effect::Deny
)
1479 rgw::IAM::PolicyPrincipal princ_type
= rgw::IAM::PolicyPrincipal::Other
;
1480 auto r
= eval_or_pass(dpp
, bucket_policy
, s
->env
, *s
->identity
, op
, ARN(obj
), princ_type
);
1481 if (r
== Effect::Deny
)
1484 if (!session_policies
.empty()) {
1485 auto session_policy_res
= eval_identity_or_session_policies(dpp
, session_policies
, s
->env
, op
, ARN(obj
));
1486 if (session_policy_res
== Effect::Deny
) {
1489 if (princ_type
== rgw::IAM::PolicyPrincipal::Role
) {
1490 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
1491 if ((session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
) ||
1492 (session_policy_res
== Effect::Allow
&& r
== Effect::Allow
))
1494 } else if (princ_type
== rgw::IAM::PolicyPrincipal::Session
) {
1495 //Intersection of session policy and identity policy plus bucket policy
1496 if ((session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
) || r
== Effect::Allow
)
1498 } else if (princ_type
== rgw::IAM::PolicyPrincipal::Other
) {// there was no match in the bucket policy
1499 if (session_policy_res
== Effect::Allow
&& identity_policy_res
== Effect::Allow
)
1505 if (r
== Effect::Allow
|| identity_policy_res
== Effect::Allow
)
1506 // It looks like S3 ACLs only GRANT permissions rather than
1507 // denying them, so this should be safe.
1510 const auto perm
= op_to_perm(op
);
1512 if (check_deferred_bucket_perms(dpp
, s
, obj
.bucket
, user_acl
, bucket_acl
, bucket_policy
,
1513 identity_policies
, session_policies
, RGW_DEFER_TO_BUCKET_ACLS_RECURSE
, op
) ||
1514 check_deferred_bucket_perms(dpp
, s
, obj
.bucket
, user_acl
, bucket_acl
, bucket_policy
,
1515 identity_policies
, session_policies
, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL
, rgw::IAM::s3All
)) {
1523 bool ret
= object_acl
->verify_permission(dpp
, *s
->identity
, s
->perm_mask
, perm
,
1524 nullptr, /* http_referrer */
1525 s
->bucket_access_conf
&&
1526 s
->bucket_access_conf
->ignore_public_acls());
1531 if (!s
->cct
->_conf
->rgw_enforce_swift_acls
)
1534 if ((perm
& (int)s
->perm_mask
) != perm
)
1538 if (perm
& (RGW_PERM_READ
| RGW_PERM_READ_ACP
))
1539 swift_perm
|= RGW_PERM_READ_OBJS
;
1540 if (perm
& RGW_PERM_WRITE
)
1541 swift_perm
|= RGW_PERM_WRITE_OBJS
;
1546 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1547 otherwise the mask might not cover the swift permissions bits */
1548 if (bucket_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
,
1555 return user_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
);
1558 bool verify_object_permission(const DoutPrefixProvider
* dpp
, req_state
* const s
,
1560 RGWAccessControlPolicy
* const user_acl
,
1561 RGWAccessControlPolicy
* const bucket_acl
,
1562 RGWAccessControlPolicy
* const object_acl
,
1563 const boost::optional
<Policy
>& bucket_policy
,
1564 const vector
<Policy
>& identity_policies
,
1565 const vector
<Policy
>& session_policies
,
1568 perm_state_from_req_state
ps(s
);
1569 return verify_object_permission(dpp
, &ps
, obj
,
1570 user_acl
, bucket_acl
,
1571 object_acl
, bucket_policy
,
1572 identity_policies
, session_policies
, op
);
1575 bool verify_object_permission_no_policy(const DoutPrefixProvider
* dpp
,
1576 struct perm_state_base
* const s
,
1577 RGWAccessControlPolicy
* const user_acl
,
1578 RGWAccessControlPolicy
* const bucket_acl
,
1579 RGWAccessControlPolicy
* const object_acl
,
1582 if (check_deferred_bucket_only_acl(dpp
, s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_RECURSE
, perm
) ||
1583 check_deferred_bucket_only_acl(dpp
, s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL
, RGW_PERM_FULL_CONTROL
)) {
1591 bool ret
= object_acl
->verify_permission(dpp
, *s
->identity
, s
->perm_mask
, perm
,
1592 nullptr, /* http referrer */
1593 s
->bucket_access_conf
&&
1594 s
->bucket_access_conf
->ignore_public_acls());
1599 if (!s
->cct
->_conf
->rgw_enforce_swift_acls
)
1602 if ((perm
& (int)s
->perm_mask
) != perm
)
1606 if (perm
& (RGW_PERM_READ
| RGW_PERM_READ_ACP
))
1607 swift_perm
|= RGW_PERM_READ_OBJS
;
1608 if (perm
& RGW_PERM_WRITE
)
1609 swift_perm
|= RGW_PERM_WRITE_OBJS
;
1614 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1615 otherwise the mask might not cover the swift permissions bits */
1616 if (bucket_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
,
1623 return user_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
);
1626 bool verify_object_permission_no_policy(const DoutPrefixProvider
* dpp
, req_state
*s
, int perm
)
1628 perm_state_from_req_state
ps(s
);
1630 if (!verify_requester_payer_permission(&ps
))
1633 return verify_object_permission_no_policy(dpp
,
1636 s
->bucket_acl
.get(),
1637 s
->object_acl
.get(),
1641 bool verify_object_permission(const DoutPrefixProvider
* dpp
, req_state
*s
, uint64_t op
)
1643 perm_state_from_req_state
ps(s
);
1645 return verify_object_permission(dpp
,
1647 rgw_obj(s
->bucket
->get_key(), s
->object
->get_key()),
1649 s
->bucket_acl
.get(),
1650 s
->object_acl
.get(),
1652 s
->iam_user_policies
,
1653 s
->session_policies
,
1658 int verify_object_lock(const DoutPrefixProvider
* dpp
, const rgw::sal::Attrs
& attrs
, const bool bypass_perm
, const bool bypass_governance_mode
) {
1659 auto aiter
= attrs
.find(RGW_ATTR_OBJECT_RETENTION
);
1660 if (aiter
!= attrs
.end()) {
1661 RGWObjectRetention obj_retention
;
1663 decode(obj_retention
, aiter
->second
);
1664 } catch (buffer::error
& err
) {
1665 ldpp_dout(dpp
, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl
;
1668 if (ceph::real_clock::to_time_t(obj_retention
.get_retain_until_date()) > ceph_clock_now()) {
1669 if (obj_retention
.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm
|| !bypass_governance_mode
) {
1674 aiter
= attrs
.find(RGW_ATTR_OBJECT_LEGAL_HOLD
);
1675 if (aiter
!= attrs
.end()) {
1676 RGWObjectLegalHold obj_legal_hold
;
1678 decode(obj_legal_hold
, aiter
->second
);
1679 } catch (buffer::error
& err
) {
1680 ldpp_dout(dpp
, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl
;
1683 if (obj_legal_hold
.is_enabled()) {
1698 // FIPS zeroization audit 20191115: this memset is not security related.
1699 memset(table
, -1, sizeof(table
));
1701 for (i
= '0'; i
<='9'; i
++)
1703 for (i
= 'A'; i
<='F'; i
++)
1704 table
[i
] = i
- 'A' + 0xa;
1705 for (i
= 'a'; i
<='f'; i
++)
1706 table
[i
] = i
- 'a' + 0xa;
1709 char to_num(char c
) {
1710 return table
[(int)c
];
1714 static char hex_to_num(char c
)
1716 static HexTable hex_table
;
1717 return hex_table
.to_num(c
);
1720 std::string
url_decode(const std::string_view
& src_str
, bool in_query
)
1722 std::string dest_str
;
1723 dest_str
.reserve(src_str
.length() + 1);
1725 for (auto src
= std::begin(src_str
); src
!= std::end(src_str
); ++src
) {
1727 if (!in_query
|| *src
!= '+') {
1731 dest_str
.push_back(*src
);
1733 dest_str
.push_back(' ');
1736 /* 3 == strlen("%%XX") */
1737 if (std::distance(src
, std::end(src_str
)) < 3) {
1742 const char c1
= hex_to_num(*src
++);
1743 const char c2
= hex_to_num(*src
);
1744 if (c1
< 0 || c2
< 0) {
1745 return std::string();
1747 dest_str
.push_back(c1
<< 4 | c2
);
1755 void rgw_uri_escape_char(char c
, string
& dst
)
1758 snprintf(buf
, sizeof(buf
), "%%%.2X", (int)(unsigned char)c
);
1762 static bool char_needs_url_encoding(char c
)
1764 if (c
<= 0x20 || c
>= 0x7f)
1794 void url_encode(const string
& src
, string
& dst
, bool encode_slash
)
1796 const char *p
= src
.c_str();
1797 for (unsigned i
= 0; i
< src
.size(); i
++, p
++) {
1798 if ((!encode_slash
&& *p
== 0x2F) || !char_needs_url_encoding(*p
)) {
1801 rgw_uri_escape_char(*p
, dst
);
1806 std::string
url_encode(const std::string
& src
, bool encode_slash
)
1809 url_encode(src
, dst
, encode_slash
);
1814 std::string
url_remove_prefix(const std::string
& url
)
1816 std::string dst
= url
;
1817 auto pos
= dst
.find("http://");
1818 if (pos
== std::string::npos
) {
1819 pos
= dst
.find("https://");
1820 if (pos
!= std::string::npos
) {
1823 pos
= dst
.find("www.");
1824 if (pos
!= std::string::npos
) {
1835 string
rgw_trim_whitespace(const string
& src
)
1842 for (; start
!= (int)src
.size(); start
++) {
1843 if (!isspace(src
[start
]))
1847 int end
= src
.size() - 1;
1852 for (; end
> start
; end
--) {
1853 if (!isspace(src
[end
]))
1857 return src
.substr(start
, end
- start
+ 1);
1860 std::string_view
rgw_trim_whitespace(const std::string_view
& src
)
1862 std::string_view res
= src
;
1864 while (res
.size() > 0 && std::isspace(res
.front())) {
1865 res
.remove_prefix(1);
1867 while (res
.size() > 0 && std::isspace(res
.back())) {
1868 res
.remove_suffix(1);
1873 string
rgw_trim_quotes(const string
& val
)
1875 string s
= rgw_trim_whitespace(val
);
1880 int end
= s
.size() - 1;
1881 int quotes_count
= 0;
1883 if (s
[start
] == '"') {
1887 if (s
[end
] == '"') {
1891 if (quotes_count
== 2) {
1892 return s
.substr(start
, end
- start
+ 1);
1897 static struct rgw_name_to_flag cap_names
[] = { {"*", RGW_CAP_ALL
},
1898 {"read", RGW_CAP_READ
},
1899 {"write", RGW_CAP_WRITE
},
1902 static int rgw_parse_list_of_flags(struct rgw_name_to_flag
*mapping
,
1903 const string
& str
, uint32_t *perm
)
1906 get_str_list(str
, strs
);
1907 list
<string
>::iterator iter
;
1909 for (iter
= strs
.begin(); iter
!= strs
.end(); ++iter
) {
1911 for (int i
= 0; mapping
[i
].type_name
; i
++) {
1912 if (s
.compare(mapping
[i
].type_name
) == 0)
1913 v
|= mapping
[i
].flag
;
1921 int RGWUserCaps::parse_cap_perm(const string
& str
, uint32_t *perm
)
1923 return rgw_parse_list_of_flags(cap_names
, str
, perm
);
1926 int RGWUserCaps::get_cap(const string
& cap
, string
& type
, uint32_t *pperm
)
1928 int pos
= cap
.find('=');
1930 type
= rgw_trim_whitespace(cap
.substr(0, pos
));
1933 if (!is_valid_cap_type(type
))
1934 return -ERR_INVALID_CAP
;
1938 if (pos
< (int)cap
.size() - 1) {
1939 cap_perm
= cap
.substr(pos
+ 1);
1940 int r
= RGWUserCaps::parse_cap_perm(cap_perm
, &perm
);
1950 int RGWUserCaps::add_cap(const string
& cap
)
1955 int r
= get_cap(cap
, type
, &perm
);
1964 int RGWUserCaps::remove_cap(const string
& cap
)
1969 int r
= get_cap(cap
, type
, &perm
);
1973 map
<string
, uint32_t>::iterator iter
= caps
.find(type
);
1974 if (iter
== caps
.end())
1977 uint32_t& old_perm
= iter
->second
;
1985 int RGWUserCaps::add_from_string(const string
& str
)
1989 auto end
= str
.find(';', start
);
1990 if (end
== string::npos
)
1993 int r
= add_cap(str
.substr(start
, end
- start
));
1998 } while (start
< (int)str
.size());
2003 int RGWUserCaps::remove_from_string(const string
& str
)
2007 auto end
= str
.find(';', start
);
2008 if (end
== string::npos
)
2011 int r
= remove_cap(str
.substr(start
, end
- start
));
2016 } while (start
< (int)str
.size());
2021 void RGWUserCaps::dump(Formatter
*f
) const
2026 void RGWUserCaps::dump(Formatter
*f
, const char *name
) const
2028 f
->open_array_section(name
);
2029 map
<string
, uint32_t>::const_iterator iter
;
2030 for (iter
= caps
.begin(); iter
!= caps
.end(); ++iter
)
2032 f
->open_object_section("cap");
2033 f
->dump_string("type", iter
->first
);
2034 uint32_t perm
= iter
->second
;
2036 for (int i
=0; cap_names
[i
].type_name
; i
++) {
2037 if ((perm
& cap_names
[i
].flag
) == cap_names
[i
].flag
) {
2038 if (perm_str
.size())
2039 perm_str
.append(", ");
2041 perm_str
.append(cap_names
[i
].type_name
);
2042 perm
&= ~cap_names
[i
].flag
;
2045 if (perm_str
.empty())
2046 perm_str
= "<none>";
2048 f
->dump_string("perm", perm_str
);
2059 void decode_json(JSONObj
*obj
) {
2060 JSONDecoder::decode_json("type", type
, obj
);
2062 JSONDecoder::decode_json("perm", perm_str
, obj
);
2063 if (RGWUserCaps::parse_cap_perm(perm_str
, &perm
) < 0) {
2064 throw JSONDecoder::err("failed to parse permissions");
2069 void RGWUserCaps::decode_json(JSONObj
*obj
)
2071 list
<RGWUserCap
> caps_list
;
2072 decode_json_obj(caps_list
, obj
);
2074 list
<RGWUserCap
>::iterator iter
;
2075 for (iter
= caps_list
.begin(); iter
!= caps_list
.end(); ++iter
) {
2076 RGWUserCap
& cap
= *iter
;
2077 caps
[cap
.type
] = cap
.perm
;
2081 int RGWUserCaps::check_cap(const string
& cap
, uint32_t perm
) const
2083 auto iter
= caps
.find(cap
);
2085 if ((iter
== caps
.end()) ||
2086 (iter
->second
& perm
) != perm
) {
2093 bool RGWUserCaps::is_valid_cap_type(const string
& tp
)
2095 static const char *cap_type
[] = { "user",
2111 for (unsigned int i
= 0; i
< sizeof(cap_type
) / sizeof(char *); ++i
) {
2112 if (tp
.compare(cap_type
[i
]) == 0) {
2120 void rgw_pool::from_str(const string
& s
)
2122 size_t pos
= rgw_unescape_str(s
, 0, '\\', ':', &name
);
2123 if (pos
!= string::npos
) {
2124 pos
= rgw_unescape_str(s
, pos
, '\\', ':', &ns
);
2125 /* ignore return; if pos != string::npos it means that we had a colon
2126 * in the middle of ns that wasn't escaped, we're going to stop there
2131 string
rgw_pool::to_str() const
2134 rgw_escape_str(name
, '\\', ':', &esc_name
);
2139 rgw_escape_str(ns
, '\\', ':', &esc_ns
);
2140 return esc_name
+ ":" + esc_ns
;
2143 void rgw_raw_obj::decode_from_rgw_obj(bufferlist::const_iterator
& bl
)
2147 decode(old_obj
, bl
);
2149 get_obj_bucket_and_oid_loc(old_obj
, oid
, loc
);
2150 pool
= old_obj
.get_explicit_data_pool();
2153 static struct rgw_name_to_flag op_type_mapping
[] = { {"*", RGW_OP_TYPE_ALL
},
2154 {"read", RGW_OP_TYPE_READ
},
2155 {"write", RGW_OP_TYPE_WRITE
},
2156 {"delete", RGW_OP_TYPE_DELETE
},
2160 int rgw_parse_op_type_list(const string
& str
, uint32_t *perm
)
2162 return rgw_parse_list_of_flags(op_type_mapping
, str
, perm
);
2165 bool match_policy(std::string_view pattern
, std::string_view input
,
2168 const uint32_t flag2
= flag
& (MATCH_POLICY_ACTION
|MATCH_POLICY_ARN
) ?
2169 MATCH_CASE_INSENSITIVE
: 0;
2170 const bool colonblocks
= !(flag
& (MATCH_POLICY_RESOURCE
|
2171 MATCH_POLICY_STRING
));
2173 const auto npos
= std::string_view::npos
;
2174 std::string_view::size_type last_pos_input
= 0, last_pos_pattern
= 0;
2176 auto cur_pos_input
= colonblocks
? input
.find(":", last_pos_input
) : npos
;
2177 auto cur_pos_pattern
=
2178 colonblocks
? pattern
.find(":", last_pos_pattern
) : npos
;
2180 auto substr_input
= input
.substr(last_pos_input
, cur_pos_input
);
2181 auto substr_pattern
= pattern
.substr(last_pos_pattern
, cur_pos_pattern
);
2183 if (!match_wildcards(substr_pattern
, substr_input
, flag2
))
2186 if (cur_pos_pattern
== npos
)
2187 return cur_pos_input
== npos
;
2188 if (cur_pos_input
== npos
)
2191 last_pos_pattern
= cur_pos_pattern
+ 1;
2192 last_pos_input
= cur_pos_input
+ 1;
2197 * make attrs look-like-this
2198 * converts underscores to dashes
2200 string
lowercase_dash_http_attr(const string
& orig
)
2202 const char *s
= orig
.c_str();
2203 char buf
[orig
.size() + 1];
2204 buf
[orig
.size()] = '\0';
2206 for (size_t i
= 0; i
< orig
.size(); ++i
, ++s
) {
2212 buf
[i
] = tolower(*s
);
2219 * make attrs Look-Like-This
2220 * converts underscores to dashes
2222 string
camelcase_dash_http_attr(const string
& orig
)
2224 const char *s
= orig
.c_str();
2225 char buf
[orig
.size() + 1];
2226 buf
[orig
.size()] = '\0';
2228 bool last_sep
= true;
2230 for (size_t i
= 0; i
< orig
.size(); ++i
, ++s
) {
2239 buf
[i
] = toupper(*s
);
2241 buf
[i
] = tolower(*s
);
2249 RGWBucketInfo::RGWBucketInfo()
2253 RGWBucketInfo::~RGWBucketInfo()
2257 void RGWBucketInfo::encode(bufferlist
& bl
) const {
2258 ENCODE_START(23, 4, bl
);
2260 encode(owner
.id
, bl
);
2262 encode(zonegroup
, bl
);
2263 uint64_t ct
= real_clock::to_time_t(creation_time
);
2265 encode(placement_rule
, bl
);
2266 encode(has_instance_obj
, bl
);
2268 encode(requester_pays
, bl
);
2269 encode(owner
.tenant
, bl
);
2270 encode(has_website
, bl
);
2272 encode(website_conf
, bl
);
2274 encode(swift_versioning
, bl
);
2275 if (swift_versioning
) {
2276 encode(swift_ver_location
, bl
);
2278 encode(creation_time
, bl
);
2279 encode(mdsearch_config
, bl
);
2280 encode(reshard_status
, bl
);
2281 encode(new_bucket_instance_id
, bl
);
2282 if (obj_lock_enabled()) {
2283 encode(obj_lock
, bl
);
2285 bool has_sync_policy
= !empty_sync_policy();
2286 encode(has_sync_policy
, bl
);
2287 if (has_sync_policy
) {
2288 encode(*sync_policy
, bl
);
2291 encode(owner
.ns
, bl
);
2295 void RGWBucketInfo::decode(bufferlist::const_iterator
& bl
) {
2296 DECODE_START_LEGACY_COMPAT_LEN_32(23, 4, 4, bl
);
2298 if (struct_v
>= 2) {
2306 decode(zonegroup
, bl
);
2307 if (struct_v
>= 6) {
2311 creation_time
= ceph::real_clock::from_time_t((time_t)ct
);
2314 decode(placement_rule
, bl
);
2316 decode(has_instance_obj
, bl
);
2319 static constexpr uint8_t new_layout_v
= 22;
2320 if (struct_v
>= 10 && struct_v
< new_layout_v
)
2321 decode(layout
.current_index
.layout
.normal
.num_shards
, bl
);
2322 if (struct_v
>= 11 && struct_v
< new_layout_v
)
2323 decode(layout
.current_index
.layout
.normal
.hash_type
, bl
);
2325 decode(requester_pays
, bl
);
2327 decode(owner
.tenant
, bl
);
2328 if (struct_v
>= 14) {
2329 decode(has_website
, bl
);
2331 decode(website_conf
, bl
);
2333 website_conf
= RGWBucketWebsiteConf();
2336 if (struct_v
>= 15 && struct_v
< new_layout_v
) {
2339 layout
.current_index
.layout
.type
= (rgw::BucketIndexType
)it
;
2341 layout
.current_index
.layout
.type
= rgw::BucketIndexType::Normal
;
2343 swift_versioning
= false;
2344 swift_ver_location
.clear();
2345 if (struct_v
>= 16) {
2346 decode(swift_versioning
, bl
);
2347 if (swift_versioning
) {
2348 decode(swift_ver_location
, bl
);
2351 if (struct_v
>= 17) {
2352 decode(creation_time
, bl
);
2354 if (struct_v
>= 18) {
2355 decode(mdsearch_config
, bl
);
2357 if (struct_v
>= 19) {
2358 decode(reshard_status
, bl
);
2359 decode(new_bucket_instance_id
, bl
);
2361 if (struct_v
>= 20 && obj_lock_enabled()) {
2362 decode(obj_lock
, bl
);
2364 if (struct_v
>= 21) {
2365 decode(sync_policy
, bl
);
2367 if (struct_v
>= 22) {
2370 if (struct_v
>= 23) {
2371 decode(owner
.ns
, bl
);
2374 if (layout
.logs
.empty() &&
2375 layout
.current_index
.layout
.type
== rgw::BucketIndexType::Normal
) {
2376 layout
.logs
.push_back(rgw::log_layout_from_index(0, layout
.current_index
));
2381 void RGWBucketInfo::set_sync_policy(rgw_sync_policy_info
&& policy
)
2383 sync_policy
= std::move(policy
);
2386 bool RGWBucketInfo::empty_sync_policy() const
2392 return sync_policy
->empty();
2396 struct rgw_placement_rule
;
2399 void decode_json_obj(rgw_pool
& pool
, JSONObj
*obj
)
2402 decode_json_obj(s
, obj
);
2406 void encode_json(const char *name
, const rgw_placement_rule
& r
, Formatter
*f
)
2408 encode_json(name
, r
.to_str(), f
);
2411 void encode_json(const char *name
, const rgw_pool
& pool
, Formatter
*f
)
2413 f
->dump_string(name
, pool
.to_str());
2416 void encode_json(const char *name
, const RGWUserCaps
& val
, Formatter
*f
)
2421 void RGWBucketEnt::generate_test_instances(list
<RGWBucketEnt
*>& o
)
2423 RGWBucketEnt
*e
= new RGWBucketEnt
;
2424 init_bucket(&e
->bucket
, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2426 e
->size_rounded
= 4096;
2429 o
.push_back(new RGWBucketEnt
);
2432 void RGWBucketEnt::dump(Formatter
*f
) const
2434 encode_json("bucket", bucket
, f
);
2435 encode_json("size", size
, f
);
2436 encode_json("size_rounded", size_rounded
, f
);
2437 utime_t
ut(creation_time
);
2438 encode_json("mtime", ut
, f
); /* mtime / creation time discrepency needed for backward compatibility */
2439 encode_json("count", count
, f
);
2440 encode_json("placement_rule", placement_rule
.to_str(), f
);
2443 void rgw_obj::generate_test_instances(list
<rgw_obj
*>& o
)
2446 init_bucket(&b
, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2447 rgw_obj
*obj
= new rgw_obj(b
, "object");
2449 o
.push_back(new rgw_obj
);
2452 void rgw_bucket_placement::dump(Formatter
*f
) const
2454 encode_json("bucket", bucket
, f
);
2455 encode_json("placement_rule", placement_rule
, f
);
2458 void RGWBucketInfo::generate_test_instances(list
<RGWBucketInfo
*>& o
)
2460 // Since things without a log will have one synthesized on decode,
2461 // ensure the things we attempt to encode will have one added so we
2462 // round-trip properly.
2463 auto gen_layout
= [](rgw::BucketLayout
& l
) {
2464 l
.current_index
.gen
= 0;
2465 l
.current_index
.layout
.normal
.hash_type
= rgw::BucketHashType::Mod
;
2466 l
.current_index
.layout
.type
= rgw::BucketIndexType::Normal
;
2467 l
.current_index
.layout
.normal
.num_shards
= 11;
2468 l
.logs
.push_back(log_layout_from_index(
2469 l
.current_index
.gen
,
2474 RGWBucketInfo
*i
= new RGWBucketInfo
;
2475 init_bucket(&i
->bucket
, "tenant", "bucket", "pool", ".index_pool", "marker", "10");
2477 i
->flags
= BUCKET_SUSPENDED
;
2478 gen_layout(i
->layout
);
2480 i
= new RGWBucketInfo
;
2481 gen_layout(i
->layout
);
2485 void RGWBucketInfo::dump(Formatter
*f
) const
2487 encode_json("bucket", bucket
, f
);
2488 utime_t
ut(creation_time
);
2489 encode_json("creation_time", ut
, f
);
2490 encode_json("owner", owner
.to_str(), f
);
2491 encode_json("flags", flags
, f
);
2492 encode_json("zonegroup", zonegroup
, f
);
2493 encode_json("placement_rule", placement_rule
, f
);
2494 encode_json("has_instance_obj", has_instance_obj
, f
);
2495 encode_json("quota", quota
, f
);
2496 encode_json("num_shards", layout
.current_index
.layout
.normal
.num_shards
, f
);
2497 encode_json("bi_shard_hash_type", (uint32_t)layout
.current_index
.layout
.normal
.hash_type
, f
);
2498 encode_json("requester_pays", requester_pays
, f
);
2499 encode_json("has_website", has_website
, f
);
2501 encode_json("website_conf", website_conf
, f
);
2503 encode_json("swift_versioning", swift_versioning
, f
);
2504 encode_json("swift_ver_location", swift_ver_location
, f
);
2505 encode_json("index_type", (uint32_t)layout
.current_index
.layout
.type
, f
);
2506 encode_json("mdsearch_config", mdsearch_config
, f
);
2507 encode_json("reshard_status", (int)reshard_status
, f
);
2508 encode_json("new_bucket_instance_id", new_bucket_instance_id
, f
);
2509 if (!empty_sync_policy()) {
2510 encode_json("sync_policy", *sync_policy
, f
);
2514 void RGWBucketInfo::decode_json(JSONObj
*obj
) {
2515 JSONDecoder::decode_json("bucket", bucket
, obj
);
2517 JSONDecoder::decode_json("creation_time", ut
, obj
);
2518 creation_time
= ut
.to_real_time();
2519 JSONDecoder::decode_json("owner", owner
, obj
);
2520 JSONDecoder::decode_json("flags", flags
, obj
);
2521 JSONDecoder::decode_json("zonegroup", zonegroup
, obj
);
2522 /* backward compatability with region */
2523 if (zonegroup
.empty()) {
2524 JSONDecoder::decode_json("region", zonegroup
, obj
);
2527 JSONDecoder::decode_json("placement_rule", pr
, obj
);
2528 placement_rule
.from_str(pr
);
2529 JSONDecoder::decode_json("has_instance_obj", has_instance_obj
, obj
);
2530 JSONDecoder::decode_json("quota", quota
, obj
);
2531 JSONDecoder::decode_json("num_shards", layout
.current_index
.layout
.normal
.num_shards
, obj
);
2533 JSONDecoder::decode_json("bi_shard_hash_type", hash_type
, obj
);
2534 layout
.current_index
.layout
.normal
.hash_type
= static_cast<rgw::BucketHashType
>(hash_type
);
2535 JSONDecoder::decode_json("requester_pays", requester_pays
, obj
);
2536 JSONDecoder::decode_json("has_website", has_website
, obj
);
2538 JSONDecoder::decode_json("website_conf", website_conf
, obj
);
2540 JSONDecoder::decode_json("swift_versioning", swift_versioning
, obj
);
2541 JSONDecoder::decode_json("swift_ver_location", swift_ver_location
, obj
);
2543 JSONDecoder::decode_json("index_type", it
, obj
);
2544 layout
.current_index
.layout
.type
= (rgw::BucketIndexType
)it
;
2545 JSONDecoder::decode_json("mdsearch_config", mdsearch_config
, obj
);
2547 JSONDecoder::decode_json("reshard_status", rs
, obj
);
2548 reshard_status
= (cls_rgw_reshard_status
)rs
;
2550 rgw_sync_policy_info sp
;
2551 JSONDecoder::decode_json("sync_policy", sp
, obj
);
2553 set_sync_policy(std::move(sp
));
2557 void RGWUserInfo::generate_test_instances(list
<RGWUserInfo
*>& o
)
2559 RGWUserInfo
*i
= new RGWUserInfo
;
2560 i
->user_id
= "user_id";
2561 i
->display_name
= "display_name";
2562 i
->user_email
= "user@email";
2563 RGWAccessKey k1
, k2
;
2567 k2
.subuser
= "subuser";
2571 i
->access_keys
[k1
.id
] = k1
;
2572 i
->swift_keys
[k2
.id
] = k2
;
2573 i
->subusers
[u
.name
] = u
;
2576 o
.push_back(new RGWUserInfo
);
2579 static void user_info_dump_subuser(const char *name
, const RGWSubUser
& subuser
, Formatter
*f
, void *parent
)
2581 RGWUserInfo
*info
= static_cast<RGWUserInfo
*>(parent
);
2582 subuser
.dump(f
, info
->user_id
.to_str());
2585 static void user_info_dump_key(const char *name
, const RGWAccessKey
& key
, Formatter
*f
, void *parent
)
2587 RGWUserInfo
*info
= static_cast<RGWUserInfo
*>(parent
);
2588 key
.dump(f
, info
->user_id
.to_str(), false);
2591 static void user_info_dump_swift_key(const char *name
, const RGWAccessKey
& key
, Formatter
*f
, void *parent
)
2593 RGWUserInfo
*info
= static_cast<RGWUserInfo
*>(parent
);
2594 key
.dump(f
, info
->user_id
.to_str(), true);
2597 static void decode_access_keys(map
<string
, RGWAccessKey
>& m
, JSONObj
*o
)
2604 static void decode_swift_keys(map
<string
, RGWAccessKey
>& m
, JSONObj
*o
)
2607 k
.decode_json(o
, true);
2611 static void decode_subusers(map
<string
, RGWSubUser
>& m
, JSONObj
*o
)
2619 struct rgw_flags_desc
{
2624 static struct rgw_flags_desc rgw_perms
[] = {
2625 { RGW_PERM_FULL_CONTROL
, "full-control" },
2626 { RGW_PERM_READ
| RGW_PERM_WRITE
, "read-write" },
2627 { RGW_PERM_READ
, "read" },
2628 { RGW_PERM_WRITE
, "write" },
2629 { RGW_PERM_READ_ACP
, "read-acp" },
2630 { RGW_PERM_WRITE_ACP
, "write-acp" },
2634 void rgw_perm_to_str(uint32_t mask
, char *buf
, int len
)
2636 const char *sep
= "";
2639 snprintf(buf
, len
, "<none>");
2643 uint32_t orig_mask
= mask
;
2644 for (int i
= 0; rgw_perms
[i
].mask
; i
++) {
2645 struct rgw_flags_desc
*desc
= &rgw_perms
[i
];
2646 if ((mask
& desc
->mask
) == desc
->mask
) {
2647 pos
+= snprintf(buf
+ pos
, len
- pos
, "%s%s", sep
, desc
->str
);
2651 mask
&= ~desc
->mask
;
2656 if (mask
== orig_mask
) // no change
2661 uint32_t rgw_str_to_perm(const char *str
)
2663 if (strcasecmp(str
, "") == 0)
2664 return RGW_PERM_NONE
;
2665 else if (strcasecmp(str
, "read") == 0)
2666 return RGW_PERM_READ
;
2667 else if (strcasecmp(str
, "write") == 0)
2668 return RGW_PERM_WRITE
;
2669 else if (strcasecmp(str
, "readwrite") == 0)
2670 return RGW_PERM_READ
| RGW_PERM_WRITE
;
2671 else if (strcasecmp(str
, "full") == 0)
2672 return RGW_PERM_FULL_CONTROL
;
2674 return RGW_PERM_INVALID
;
2678 static void mask_to_str(T
*mask_list
, uint32_t mask
, char *buf
, int len
)
2680 const char *sep
= "";
2683 snprintf(buf
, len
, "<none>");
2687 uint32_t orig_mask
= mask
;
2688 for (int i
= 0; mask_list
[i
].mask
; i
++) {
2689 T
*desc
= &mask_list
[i
];
2690 if ((mask
& desc
->mask
) == desc
->mask
) {
2691 pos
+= snprintf(buf
+ pos
, len
- pos
, "%s%s", sep
, desc
->str
);
2695 mask
&= ~desc
->mask
;
2700 if (mask
== orig_mask
) // no change
2705 static void perm_to_str(uint32_t mask
, char *buf
, int len
)
2707 return mask_to_str(rgw_perms
, mask
, buf
, len
);
2710 static struct rgw_flags_desc op_type_flags
[] = {
2711 { RGW_OP_TYPE_READ
, "read" },
2712 { RGW_OP_TYPE_WRITE
, "write" },
2713 { RGW_OP_TYPE_DELETE
, "delete" },
2717 void op_type_to_str(uint32_t mask
, char *buf
, int len
)
2719 return mask_to_str(op_type_flags
, mask
, buf
, len
);
2722 void RGWRateLimitInfo::decode_json(JSONObj
*obj
)
2724 JSONDecoder::decode_json("max_read_ops", max_read_ops
, obj
);
2725 JSONDecoder::decode_json("max_write_ops", max_write_ops
, obj
);
2726 JSONDecoder::decode_json("max_read_bytes", max_read_bytes
, obj
);
2727 JSONDecoder::decode_json("max_write_bytes", max_write_bytes
, obj
);
2728 JSONDecoder::decode_json("enabled", enabled
, obj
);
2731 void RGWRateLimitInfo::dump(Formatter
*f
) const
2733 f
->dump_int("max_read_ops", max_read_ops
);
2734 f
->dump_int("max_write_ops", max_write_ops
);
2735 f
->dump_int("max_read_bytes", max_read_bytes
);
2736 f
->dump_int("max_write_bytes", max_write_bytes
);
2737 f
->dump_bool("enabled", enabled
);
2740 void RGWUserInfo::dump(Formatter
*f
) const
2743 encode_json("user_id", user_id
.to_str(), f
);
2744 encode_json("display_name", display_name
, f
);
2745 encode_json("email", user_email
, f
);
2746 encode_json("suspended", (int)suspended
, f
);
2747 encode_json("max_buckets", (int)max_buckets
, f
);
2749 encode_json_map("subusers", NULL
, "subuser", NULL
, user_info_dump_subuser
,(void *)this, subusers
, f
);
2750 encode_json_map("keys", NULL
, "key", NULL
, user_info_dump_key
,(void *)this, access_keys
, f
);
2751 encode_json_map("swift_keys", NULL
, "key", NULL
, user_info_dump_swift_key
,(void *)this, swift_keys
, f
);
2753 encode_json("caps", caps
, f
);
2756 op_type_to_str(op_mask
, buf
, sizeof(buf
));
2757 encode_json("op_mask", (const char *)buf
, f
);
2759 if (system
) { /* no need to show it for every user */
2760 encode_json("system", (bool)system
, f
);
2763 encode_json("admin", (bool)admin
, f
);
2765 encode_json("default_placement", default_placement
.name
, f
);
2766 encode_json("default_storage_class", default_placement
.storage_class
, f
);
2767 encode_json("placement_tags", placement_tags
, f
);
2768 encode_json("bucket_quota", quota
.bucket_quota
, f
);
2769 encode_json("user_quota", quota
.user_quota
, f
);
2770 encode_json("temp_url_keys", temp_url_keys
, f
);
2772 string user_source_type
;
2773 switch ((RGWIdentityType
)type
) {
2775 user_source_type
= "rgw";
2778 user_source_type
= "keystone";
2781 user_source_type
= "ldap";
2784 user_source_type
= "none";
2787 user_source_type
= "none";
2790 encode_json("type", user_source_type
, f
);
2791 encode_json("mfa_ids", mfa_ids
, f
);
2794 void RGWUserInfo::decode_json(JSONObj
*obj
)
2798 JSONDecoder::decode_json("user_id", uid
, obj
, true);
2799 user_id
.from_str(uid
);
2801 JSONDecoder::decode_json("display_name", display_name
, obj
);
2802 JSONDecoder::decode_json("email", user_email
, obj
);
2804 JSONDecoder::decode_json("suspended", susp
, obj
);
2805 suspended
= (__u8
)susp
;
2806 JSONDecoder::decode_json("max_buckets", max_buckets
, obj
);
2808 JSONDecoder::decode_json("keys", access_keys
, decode_access_keys
, obj
);
2809 JSONDecoder::decode_json("swift_keys", swift_keys
, decode_swift_keys
, obj
);
2810 JSONDecoder::decode_json("subusers", subusers
, decode_subusers
, obj
);
2812 JSONDecoder::decode_json("caps", caps
, obj
);
2815 JSONDecoder::decode_json("op_mask", mask_str
, obj
);
2816 rgw_parse_op_type_list(mask_str
, &op_mask
);
2819 JSONDecoder::decode_json("system", sys
, obj
);
2822 JSONDecoder::decode_json("admin", ad
, obj
);
2824 JSONDecoder::decode_json("default_placement", default_placement
.name
, obj
);
2825 JSONDecoder::decode_json("default_storage_class", default_placement
.storage_class
, obj
);
2826 JSONDecoder::decode_json("placement_tags", placement_tags
, obj
);
2827 JSONDecoder::decode_json("bucket_quota", quota
.bucket_quota
, obj
);
2828 JSONDecoder::decode_json("user_quota", quota
.user_quota
, obj
);
2829 JSONDecoder::decode_json("temp_url_keys", temp_url_keys
, obj
);
2831 string user_source_type
;
2832 JSONDecoder::decode_json("type", user_source_type
, obj
);
2833 if (user_source_type
== "rgw") {
2835 } else if (user_source_type
== "keystone") {
2836 type
= TYPE_KEYSTONE
;
2837 } else if (user_source_type
== "ldap") {
2839 } else if (user_source_type
== "none") {
2842 JSONDecoder::decode_json("mfa_ids", mfa_ids
, obj
);
2846 void RGWSubUser::generate_test_instances(list
<RGWSubUser
*>& o
)
2848 RGWSubUser
*u
= new RGWSubUser
;
2852 o
.push_back(new RGWSubUser
);
2855 void RGWSubUser::dump(Formatter
*f
) const
2857 encode_json("id", name
, f
);
2859 perm_to_str(perm_mask
, buf
, sizeof(buf
));
2860 encode_json("permissions", (const char *)buf
, f
);
2863 void RGWSubUser::dump(Formatter
*f
, const string
& user
) const
2868 encode_json("id", s
, f
);
2870 perm_to_str(perm_mask
, buf
, sizeof(buf
));
2871 encode_json("permissions", (const char *)buf
, f
);
2874 uint32_t str_to_perm(const string
& s
)
2876 if (s
.compare("read") == 0)
2877 return RGW_PERM_READ
;
2878 else if (s
.compare("write") == 0)
2879 return RGW_PERM_WRITE
;
2880 else if (s
.compare("read-write") == 0)
2881 return RGW_PERM_READ
| RGW_PERM_WRITE
;
2882 else if (s
.compare("full-control") == 0)
2883 return RGW_PERM_FULL_CONTROL
;
2887 void RGWSubUser::decode_json(JSONObj
*obj
)
2890 JSONDecoder::decode_json("id", uid
, obj
);
2891 int pos
= uid
.find(':');
2893 name
= uid
.substr(pos
+ 1);
2895 JSONDecoder::decode_json("permissions", perm_str
, obj
);
2896 perm_mask
= str_to_perm(perm_str
);
2899 void RGWAccessKey::generate_test_instances(list
<RGWAccessKey
*>& o
)
2901 RGWAccessKey
*k
= new RGWAccessKey
;
2904 k
->subuser
= "subuser";
2906 o
.push_back(new RGWAccessKey
);
2909 void RGWAccessKey::dump(Formatter
*f
) const
2911 encode_json("access_key", id
, f
);
2912 encode_json("secret_key", key
, f
);
2913 encode_json("subuser", subuser
, f
);
2916 void RGWAccessKey::dump_plain(Formatter
*f
) const
2918 encode_json("access_key", id
, f
);
2919 encode_json("secret_key", key
, f
);
2922 void RGWAccessKey::dump(Formatter
*f
, const string
& user
, bool swift
) const
2925 if (!subuser
.empty()) {
2929 encode_json("user", u
, f
);
2931 encode_json("access_key", id
, f
);
2933 encode_json("secret_key", key
, f
);
2936 void RGWAccessKey::decode_json(JSONObj
*obj
) {
2937 JSONDecoder::decode_json("access_key", id
, obj
, true);
2938 JSONDecoder::decode_json("secret_key", key
, obj
, true);
2939 if (!JSONDecoder::decode_json("subuser", subuser
, obj
)) {
2941 JSONDecoder::decode_json("user", user
, obj
);
2942 int pos
= user
.find(':');
2944 subuser
= user
.substr(pos
+ 1);
2949 void RGWAccessKey::decode_json(JSONObj
*obj
, bool swift
) {
2955 if (!JSONDecoder::decode_json("subuser", subuser
, obj
)) {
2956 JSONDecoder::decode_json("user", id
, obj
, true);
2957 int pos
= id
.find(':');
2959 subuser
= id
.substr(pos
+ 1);
2962 JSONDecoder::decode_json("secret_key", key
, obj
, true);
2965 void RGWStorageStats::dump(Formatter
*f
) const
2967 encode_json("size", size
, f
);
2968 encode_json("size_actual", size_rounded
, f
);
2969 if (dump_utilized
) {
2970 encode_json("size_utilized", size_utilized
, f
);
2972 encode_json("size_kb", rgw_rounded_kb(size
), f
);
2973 encode_json("size_kb_actual", rgw_rounded_kb(size_rounded
), f
);
2974 if (dump_utilized
) {
2975 encode_json("size_kb_utilized", rgw_rounded_kb(size_utilized
), f
);
2977 encode_json("num_objects", num_objects
, f
);
2980 void rgw_obj_key::dump(Formatter
*f
) const
2982 encode_json("name", name
, f
);
2983 encode_json("instance", instance
, f
);
2984 encode_json("ns", ns
, f
);
2987 void rgw_obj_key::decode_json(JSONObj
*obj
)
2989 JSONDecoder::decode_json("name", name
, obj
);
2990 JSONDecoder::decode_json("instance", instance
, obj
);
2991 JSONDecoder::decode_json("ns", ns
, obj
);
2994 void rgw_raw_obj::dump(Formatter
*f
) const
2996 encode_json("pool", pool
, f
);
2997 encode_json("oid", oid
, f
);
2998 encode_json("loc", loc
, f
);
3001 void rgw_raw_obj::decode_json(JSONObj
*obj
) {
3002 JSONDecoder::decode_json("pool", pool
, obj
);
3003 JSONDecoder::decode_json("oid", oid
, obj
);
3004 JSONDecoder::decode_json("loc", loc
, obj
);
3007 void rgw_obj::dump(Formatter
*f
) const
3009 encode_json("bucket", bucket
, f
);
3010 encode_json("key", key
, f
);
3013 int rgw_bucket_parse_bucket_instance(const string
& bucket_instance
, string
*bucket_name
, string
*bucket_id
, int *shard_id
)
3015 auto pos
= bucket_instance
.rfind(':');
3016 if (pos
== string::npos
) {
3020 string first
= bucket_instance
.substr(0, pos
);
3021 string second
= bucket_instance
.substr(pos
+ 1);
3023 pos
= first
.find(':');
3025 if (pos
== string::npos
) {
3027 *bucket_name
= first
;
3028 *bucket_id
= second
;
3032 *bucket_name
= first
.substr(0, pos
);
3033 *bucket_id
= first
.substr(pos
+ 1);
3036 *shard_id
= strict_strtol(second
.c_str(), 10, &err
);
3044 boost::intrusive_ptr
<CephContext
>
3045 rgw_global_init(const std::map
<std::string
,std::string
> *defaults
,
3046 std::vector
< const char* >& args
,
3047 uint32_t module_type
, code_environment_t code_env
,
3050 // Load the config from the files, but not the mon
3051 global_pre_init(defaults
, args
, module_type
, code_env
, flags
);
3053 // Get the store backend
3054 const auto& config_store
= g_conf().get_val
<std::string
>("rgw_backend_store");
3056 if ((config_store
== "dbstore") ||
3057 (config_store
== "motr") ||
3058 (config_store
== "daos")) {
3059 // These stores don't use the mon
3060 flags
|= CINIT_FLAG_NO_MON_CONFIG
;
3063 // Finish global init, indicating we already ran pre-init
3064 return global_init(defaults
, args
, module_type
, code_env
, flags
, false);
3067 void RGWObjVersionTracker::generate_new_write_ver(CephContext
*cct
)
3069 write_version
.ver
= 1;
3072 write_version
.tag
.clear();
3073 append_rand_alpha(cct
, write_version
.tag
, write_version
.tag
, TAG_LEN
);