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"
14 #include "rgw_common.h"
16 #include "rgw_string.h"
17 #include "rgw_rados.h"
18 #include "rgw_http_errors.h"
20 #include "rgw_data_sync.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/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
39 #define dout_subsys ceph_subsys_rgw
42 using rgw::IAM::Effect
;
43 using rgw::IAM::op_to_perm
;
44 using rgw::IAM::Policy
;
46 const uint32_t RGWBucketInfo::NUM_SHARDS_BLIND_BUCKET(UINT32_MAX
);
48 rgw_http_errors
rgw_http_s3_errors({
50 { STATUS_CREATED
, {201, "Created" }},
51 { STATUS_ACCEPTED
, {202, "Accepted" }},
52 { STATUS_NO_CONTENT
, {204, "NoContent" }},
53 { STATUS_PARTIAL_CONTENT
, {206, "" }},
54 { ERR_PERMANENT_REDIRECT
, {301, "PermanentRedirect" }},
55 { ERR_WEBSITE_REDIRECT
, {301, "WebsiteRedirect" }},
56 { STATUS_REDIRECT
, {303, "" }},
57 { ERR_NOT_MODIFIED
, {304, "NotModified" }},
58 { EINVAL
, {400, "InvalidArgument" }},
59 { ERR_INVALID_REQUEST
, {400, "InvalidRequest" }},
60 { ERR_INVALID_DIGEST
, {400, "InvalidDigest" }},
61 { ERR_BAD_DIGEST
, {400, "BadDigest" }},
62 { ERR_INVALID_LOCATION_CONSTRAINT
, {400, "InvalidLocationConstraint" }},
63 { ERR_ZONEGROUP_DEFAULT_PLACEMENT_MISCONFIGURATION
, {400, "ZonegroupDefaultPlacementMisconfiguration" }},
64 { ERR_INVALID_BUCKET_NAME
, {400, "InvalidBucketName" }},
65 { ERR_INVALID_OBJECT_NAME
, {400, "InvalidObjectName" }},
66 { ERR_UNRESOLVABLE_EMAIL
, {400, "UnresolvableGrantByEmailAddress" }},
67 { ERR_INVALID_PART
, {400, "InvalidPart" }},
68 { ERR_INVALID_PART_ORDER
, {400, "InvalidPartOrder" }},
69 { ERR_REQUEST_TIMEOUT
, {400, "RequestTimeout" }},
70 { ERR_TOO_LARGE
, {400, "EntityTooLarge" }},
71 { ERR_TOO_SMALL
, {400, "EntityTooSmall" }},
72 { ERR_TOO_MANY_BUCKETS
, {400, "TooManyBuckets" }},
73 { ERR_MALFORMED_XML
, {400, "MalformedXML" }},
74 { ERR_AMZ_CONTENT_SHA256_MISMATCH
, {400, "XAmzContentSHA256Mismatch" }},
75 { ERR_MALFORMED_DOC
, {400, "MalformedPolicyDocument"}},
76 { ERR_INVALID_TAG
, {400, "InvalidTag"}},
77 { ERR_MALFORMED_ACL_ERROR
, {400, "MalformedACLError" }},
78 { ERR_INVALID_CORS_RULES_ERROR
, {400, "InvalidRequest" }},
79 { ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR
, {400, "InvalidRequest" }},
80 { ERR_INVALID_ENCRYPTION_ALGORITHM
, {400, "InvalidEncryptionAlgorithmError" }},
81 { ERR_INVALID_RETENTION_PERIOD
,{400, "InvalidRetentionPeriod"}},
82 { ERR_LIMIT_EXCEEDED
, {400, "LimitExceeded" }},
83 { ERR_LENGTH_REQUIRED
, {411, "MissingContentLength" }},
84 { EACCES
, {403, "AccessDenied" }},
85 { EPERM
, {403, "AccessDenied" }},
86 { ERR_SIGNATURE_NO_MATCH
, {403, "SignatureDoesNotMatch" }},
87 { ERR_INVALID_ACCESS_KEY
, {403, "InvalidAccessKeyId" }},
88 { ERR_USER_SUSPENDED
, {403, "UserSuspended" }},
89 { ERR_REQUEST_TIME_SKEWED
, {403, "RequestTimeTooSkewed" }},
90 { ERR_QUOTA_EXCEEDED
, {403, "QuotaExceeded" }},
91 { ERR_MFA_REQUIRED
, {403, "AccessDenied" }},
92 { ENOENT
, {404, "NoSuchKey" }},
93 { ERR_NO_SUCH_BUCKET
, {404, "NoSuchBucket" }},
94 { ERR_NO_SUCH_WEBSITE_CONFIGURATION
, {404, "NoSuchWebsiteConfiguration" }},
95 { ERR_NO_SUCH_UPLOAD
, {404, "NoSuchUpload" }},
96 { ERR_NOT_FOUND
, {404, "Not Found"}},
97 { ERR_NO_SUCH_LC
, {404, "NoSuchLifecycleConfiguration"}},
98 { ERR_NO_SUCH_BUCKET_POLICY
, {404, "NoSuchBucketPolicy"}},
99 { ERR_NO_SUCH_USER
, {404, "NoSuchUser"}},
100 { ERR_NO_ROLE_FOUND
, {404, "NoSuchEntity"}},
101 { ERR_NO_CORS_FOUND
, {404, "NoSuchCORSConfiguration"}},
102 { ERR_NO_SUCH_SUBUSER
, {404, "NoSuchSubUser"}},
103 { ERR_NO_SUCH_ENTITY
, {404, "NoSuchEntity"}},
104 { ERR_NO_SUCH_CORS_CONFIGURATION
, {404, "NoSuchCORSConfiguration"}},
105 { ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION
, {404, "ObjectLockConfigurationNotFoundError"}},
106 { ERR_METHOD_NOT_ALLOWED
, {405, "MethodNotAllowed" }},
107 { ETIMEDOUT
, {408, "RequestTimeout" }},
108 { EEXIST
, {409, "BucketAlreadyExists" }},
109 { ERR_USER_EXIST
, {409, "UserAlreadyExists" }},
110 { ERR_EMAIL_EXIST
, {409, "EmailExists" }},
111 { ERR_KEY_EXIST
, {409, "KeyExists"}},
112 { ERR_TAG_CONFLICT
, {409, "OperationAborted"}},
113 { ERR_POSITION_NOT_EQUAL_TO_LENGTH
, {409, "PositionNotEqualToLength"}},
114 { ERR_OBJECT_NOT_APPENDABLE
, {409, "ObjectNotAppendable"}},
115 { ERR_INVALID_BUCKET_STATE
, {409, "InvalidBucketState"}},
116 { ERR_INVALID_SECRET_KEY
, {400, "InvalidSecretKey"}},
117 { ERR_INVALID_KEY_TYPE
, {400, "InvalidKeyType"}},
118 { ERR_INVALID_CAP
, {400, "InvalidCapability"}},
119 { ERR_INVALID_TENANT_NAME
, {400, "InvalidTenantName" }},
120 { ENOTEMPTY
, {409, "BucketNotEmpty" }},
121 { ERR_PRECONDITION_FAILED
, {412, "PreconditionFailed" }},
122 { ERANGE
, {416, "InvalidRange" }},
123 { ERR_UNPROCESSABLE_ENTITY
, {422, "UnprocessableEntity" }},
124 { ERR_LOCKED
, {423, "Locked" }},
125 { ERR_INTERNAL_ERROR
, {500, "InternalError" }},
126 { ERR_NOT_IMPLEMENTED
, {501, "NotImplemented" }},
127 { ERR_SERVICE_UNAVAILABLE
, {503, "ServiceUnavailable"}},
128 { ERR_RATE_LIMITED
, {503, "SlowDown"}},
129 { ERR_ZERO_IN_URL
, {400, "InvalidRequest" }},
130 { ERR_NO_SUCH_TAG_SET
, {404, "NoSuchTagSetError"}},
133 rgw_http_errors
rgw_http_swift_errors({
134 { EACCES
, {403, "AccessDenied" }},
135 { EPERM
, {401, "AccessDenied" }},
136 { ENAMETOOLONG
, {400, "Metadata name too long" }},
137 { ERR_USER_SUSPENDED
, {401, "UserSuspended" }},
138 { ERR_INVALID_UTF8
, {412, "Invalid UTF8" }},
139 { ERR_BAD_URL
, {412, "Bad URL" }},
140 { ERR_NOT_SLO_MANIFEST
, {400, "Not an SLO manifest" }},
141 { ERR_QUOTA_EXCEEDED
, {413, "QuotaExceeded" }},
142 { ENOTEMPTY
, {409, "There was a conflict when trying "
143 "to complete your request." }},
144 /* FIXME(rzarzynski): we need to find a way to apply Swift's error handling
145 * procedures also for ERR_ZERO_IN_URL. This make a problem as the validation
146 * is performed very early, even before setting the req_state::proto_flags. */
147 { ERR_ZERO_IN_URL
, {412, "Invalid UTF8 or contains NULL"}},
148 { ERR_RATE_LIMITED
, {498, "Rate Limited"}},
151 rgw_http_errors
rgw_http_sts_errors({
152 { ERR_PACKED_POLICY_TOO_LARGE
, {400, "PackedPolicyTooLarge" }},
153 { ERR_INVALID_IDENTITY_TOKEN
, {400, "InvalidIdentityToken" }},
156 rgw_http_errors
rgw_http_iam_errors({
157 { ERR_ROLE_EXISTS
, {409, "EntityAlreadyExists"}},
158 { ERR_DELETE_CONFLICT
, {409, "DeleteConflict"}},
161 using namespace ceph::crypto
;
180 return (http_ret
== 200);
186 return !(http_ret
>= 200 && http_ret
<= 399);
189 // The requestURI transferred from the frontend can be abs_path or absoluteURI
190 // If it is absoluteURI, we should adjust it to abs_path for the following
191 // S3 authorization and some other processes depending on the requestURI
192 // The absoluteURI can start with "http://", "https://", "ws://" or "wss://"
193 static string
get_abs_path(const string
& request_uri
) {
194 const static string ABS_PREFIXS
[] = {"http://", "https://", "ws://", "wss://"};
196 for (int i
= 0; i
< 4; ++i
) {
197 if (boost::algorithm::starts_with(request_uri
, ABS_PREFIXS
[i
])) {
202 if (!isAbs
) { // it is not a valid absolute uri
205 size_t beg_pos
= request_uri
.find("://") + 3;
206 size_t len
= request_uri
.size();
207 beg_pos
= request_uri
.find('/', beg_pos
);
208 if (beg_pos
== string::npos
) return request_uri
;
209 return request_uri
.substr(beg_pos
, len
- beg_pos
);
212 req_info::req_info(CephContext
*cct
, const class RGWEnv
*env
) : env(env
) {
213 method
= env
->get("REQUEST_METHOD", "");
214 script_uri
= env
->get("SCRIPT_URI", cct
->_conf
->rgw_script_uri
.c_str());
215 request_uri
= env
->get("REQUEST_URI", cct
->_conf
->rgw_request_uri
.c_str());
216 if (request_uri
[0] != '/') {
217 request_uri
= get_abs_path(request_uri
);
219 auto pos
= request_uri
.find('?');
220 if (pos
!= string::npos
) {
221 request_params
= request_uri
.substr(pos
+ 1);
222 request_uri
= request_uri
.substr(0, pos
);
224 request_params
= env
->get("QUERY_STRING", "");
226 host
= env
->get("HTTP_HOST", "");
228 // strip off any trailing :port from host (added by CrossFTP and maybe others)
229 size_t colon_offset
= host
.find_last_of(':');
230 if (colon_offset
!= string::npos
) {
231 bool all_digits
= true;
232 for (unsigned i
= colon_offset
+ 1; i
< host
.size(); ++i
) {
233 if (!isdigit(host
[i
])) {
239 host
.resize(colon_offset
);
244 void req_info::rebuild_from(req_info
& src
)
247 script_uri
= src
.script_uri
;
249 if (src
.effective_uri
.empty()) {
250 request_uri
= src
.request_uri
;
252 request_uri
= src
.effective_uri
;
254 effective_uri
.clear();
257 x_meta_map
= src
.x_meta_map
;
258 x_meta_map
.erase("x-amz-date");
262 req_state::req_state(CephContext
* _cct
, RGWEnv
* e
, rgw::sal::RGWUser
* u
, uint64_t id
)
263 : cct(_cct
), user(u
),
264 info(_cct
, e
), id(id
)
266 enable_ops_log
= e
->get_enable_ops_log();
267 enable_usage_log
= e
->get_enable_usage_log();
268 defer_to_bucket_acls
= e
->get_defer_to_bucket_acls();
273 req_state::~req_state() {
277 std::ostream
& req_state::gen_prefix(std::ostream
& out
) const
279 auto p
= out
.precision();
280 return out
<< "req " << id
<< ' '
281 << std::setprecision(3) << std::fixed
<< time_elapsed() // '0.123s'
282 << std::setprecision(p
) << std::defaultfloat
<< ' ';
285 bool search_err(rgw_http_errors
& errs
, int err_no
, int& http_ret
, string
& code
)
287 auto r
= errs
.find(err_no
);
288 if (r
!= errs
.end()) {
289 http_ret
= r
->second
.first
;
290 code
= r
->second
.second
;
296 void set_req_state_err(struct rgw_err
& err
, /* out */
298 const int prot_flags
) /* in */
305 if (prot_flags
& RGW_REST_SWIFT
) {
306 if (search_err(rgw_http_swift_errors
, err_no
, err
.http_ret
, err
.err_code
))
310 if (prot_flags
& RGW_REST_STS
) {
311 if (search_err(rgw_http_sts_errors
, err_no
, err
.http_ret
, err
.err_code
))
315 if (prot_flags
& RGW_REST_IAM
) {
316 if (search_err(rgw_http_iam_errors
, err_no
, err
.http_ret
, err
.err_code
))
320 //Default to searching in s3 errors
321 if (search_err(rgw_http_s3_errors
, err_no
, err
.http_ret
, err
.err_code
))
323 dout(0) << "WARNING: set_req_state_err err_no=" << err_no
324 << " resorting to 500" << dendl
;
327 err
.err_code
= "UnknownError";
330 void set_req_state_err(struct req_state
* s
, int err_no
, const string
& err_msg
)
333 set_req_state_err(s
, err_no
);
334 if (s
->prot_flags
& RGW_REST_SWIFT
&& !err_msg
.empty()) {
335 /* TODO(rzarzynski): there never ever should be a check like this one.
336 * It's here only for the sake of the patch's backportability. Further
337 * commits will move the logic to a per-RGWHandler replacement of
338 * the end_header() function. Alternativaly, we might consider making
339 * that just for the dump(). Please take a look on @cbodley's comments
340 * in PR #10690 (https://github.com/ceph/ceph/pull/10690). */
341 s
->err
.err_code
= err_msg
;
343 s
->err
.message
= err_msg
;
348 void set_req_state_err(struct req_state
* s
, int err_no
)
351 set_req_state_err(s
->err
, err_no
, s
->prot_flags
);
355 void dump(struct req_state
* s
)
357 if (s
->format
!= RGW_FORMAT_HTML
)
358 s
->formatter
->open_object_section("Error");
359 if (!s
->err
.err_code
.empty())
360 s
->formatter
->dump_string("Code", s
->err
.err_code
);
361 if (!s
->err
.message
.empty())
362 s
->formatter
->dump_string("Message", s
->err
.message
);
363 if (!s
->bucket_name
.empty()) // TODO: connect to expose_bucket
364 s
->formatter
->dump_string("BucketName", s
->bucket_name
);
365 if (!s
->trans_id
.empty()) // TODO: connect to expose_bucket or another toggle
366 s
->formatter
->dump_string("RequestId", s
->trans_id
);
367 s
->formatter
->dump_string("HostId", s
->host_id
);
368 if (s
->format
!= RGW_FORMAT_HTML
)
369 s
->formatter
->close_section();
377 #define STR_LEN_ENTRY(s) { s, sizeof(s) - 1 }
379 struct str_len meta_prefixes
[] = { STR_LEN_ENTRY("HTTP_X_AMZ"),
380 STR_LEN_ENTRY("HTTP_X_GOOG"),
381 STR_LEN_ENTRY("HTTP_X_DHO"),
382 STR_LEN_ENTRY("HTTP_X_RGW"),
383 STR_LEN_ENTRY("HTTP_X_OBJECT"),
384 STR_LEN_ENTRY("HTTP_X_CONTAINER"),
385 STR_LEN_ENTRY("HTTP_X_ACCOUNT"),
388 void req_info::init_meta_info(bool *found_bad_meta
)
392 for (const auto& kv
: env
->get_map()) {
394 const string
& header_name
= kv
.first
;
395 const string
& val
= kv
.second
;
396 for (int prefix_num
= 0; (prefix
= meta_prefixes
[prefix_num
].str
) != NULL
; prefix_num
++) {
397 int len
= meta_prefixes
[prefix_num
].len
;
398 const char *p
= header_name
.c_str();
399 if (strncmp(p
, prefix
, len
) == 0) {
400 dout(10) << "meta>> " << p
<< dendl
;
401 const char *name
= p
+len
; /* skip the prefix */
402 int name_len
= header_name
.size() - len
;
404 if (found_bad_meta
&& strncmp(name
, "_META_", name_len
) == 0)
405 *found_bad_meta
= true;
407 char name_low
[meta_prefixes
[0].len
+ name_len
+ 1];
408 snprintf(name_low
, meta_prefixes
[0].len
- 5 + name_len
+ 1, "%s%s", meta_prefixes
[0].str
+ 5 /* skip HTTP_ */, name
); // normalize meta prefix
410 for (j
= 0; name_low
[j
]; j
++) {
411 if (name_low
[j
] != '_')
412 name_low
[j
] = tolower(name_low
[j
]);
418 auto it
= x_meta_map
.find(name_low
);
419 if (it
!= x_meta_map
.end()) {
420 string old
= it
->second
;
421 boost::algorithm::trim_right(old
);
424 x_meta_map
[name_low
] = old
;
426 x_meta_map
[name_low
] = val
;
431 for (const auto& kv
: x_meta_map
) {
432 dout(10) << "x>> " << kv
.first
<< ":" << rgw::crypt_sanitize::x_meta_map
{kv
.first
, kv
.second
} << dendl
;
436 std::ostream
& operator<<(std::ostream
& oss
, const rgw_err
&err
)
438 oss
<< "rgw_err(http_ret=" << err
.http_ret
<< ", err_code='" << err
.err_code
<< "') ";
442 void rgw_add_amz_meta_header(
443 meta_map_t
& x_meta_map
,
444 const std::string
& k
,
445 const std::string
& v
)
447 auto it
= x_meta_map
.find(k
);
448 if (it
!= x_meta_map
.end()) {
449 std::string old
= it
->second
;
450 boost::algorithm::trim_right(old
);
459 string
rgw_string_unquote(const string
& s
)
461 if (s
[0] != '"' || s
.size() < 2)
465 for (len
= s
.size(); len
> 2; --len
) {
466 if (s
[len
- 1] != ' ')
473 return s
.substr(1, len
- 2);
476 static bool check_str_end(const char *s
)
489 static bool check_gmt_end(const char *s
)
494 while (isspace(*s
)) {
498 /* check for correct timezone */
499 if ((strncmp(s
, "GMT", 3) != 0) &&
500 (strncmp(s
, "UTC", 3) != 0)) {
507 static bool parse_rfc850(const char *s
, struct tm
*t
)
509 // FIPS zeroization audit 20191115: this memset is not security related.
510 memset(t
, 0, sizeof(*t
));
511 return check_gmt_end(strptime(s
, "%A, %d-%b-%y %H:%M:%S ", t
));
514 static bool parse_asctime(const char *s
, struct tm
*t
)
516 // FIPS zeroization audit 20191115: this memset is not security related.
517 memset(t
, 0, sizeof(*t
));
518 return check_str_end(strptime(s
, "%a %b %d %H:%M:%S %Y", t
));
521 static bool parse_rfc1123(const char *s
, struct tm
*t
)
523 // FIPS zeroization audit 20191115: this memset is not security related.
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 // FIPS zeroization audit 20191115: this memset is not security related.
531 memset(t
, 0, sizeof(*t
));
532 return check_str_end(strptime(s
, "%a, %d %b %Y %H:%M:%S %z", t
));
535 bool parse_rfc2616(const char *s
, struct tm
*t
)
537 return parse_rfc850(s
, t
) || parse_asctime(s
, t
) || parse_rfc1123(s
, t
) || parse_rfc1123_alt(s
,t
);
540 bool parse_iso8601(const char *s
, struct tm
*t
, uint32_t *pns
, bool extended_format
)
542 // FIPS zeroization audit 20191115: this memset is not security related.
543 memset(t
, 0, sizeof(*t
));
549 if (extended_format
) {
550 p
= strptime(s
, "%Y-%m-%dT%T", t
);
552 p
= strptime(s
, "%Y-%m-%d %T", t
);
555 p
= strptime(s
, "%Y%m%dT%H%M%S", t
);
558 dout(0) << "parse_iso8601 failed" << dendl
;
561 const boost::string_view str
= rgw_trim_whitespace(boost::string_view(p
));
562 int len
= str
.size();
564 if (len
== 0 || (len
== 1 && str
[0] == 'Z'))
572 boost::string_view nsstr
= str
.substr(1, len
- 2);
573 int r
= stringtoul(nsstr
.to_string(), &ms
);
581 if (nsstr
.size() > 9) {
582 nsstr
= nsstr
.substr(0, 9);
585 uint64_t mul_table
[] = { 0,
597 *pns
= ms
* mul_table
[nsstr
.size()];
602 int parse_key_value(string
& in_str
, const char *delim
, string
& key
, string
& val
)
607 auto pos
= in_str
.find(delim
);
608 if (pos
== string::npos
)
611 key
= rgw_trim_whitespace(in_str
.substr(0, pos
));
612 val
= rgw_trim_whitespace(in_str
.substr(pos
+ 1));
617 int parse_key_value(string
& in_str
, string
& key
, string
& val
)
619 return parse_key_value(in_str
, "=", key
,val
);
622 boost::optional
<std::pair
<boost::string_view
, boost::string_view
>>
623 parse_key_value(const boost::string_view
& in_str
,
624 const boost::string_view
& delim
)
626 const size_t pos
= in_str
.find(delim
);
627 if (pos
== boost::string_view::npos
) {
631 const auto key
= rgw_trim_whitespace(in_str
.substr(0, pos
));
632 const auto val
= rgw_trim_whitespace(in_str
.substr(pos
+ 1));
634 return std::make_pair(key
, val
);
637 boost::optional
<std::pair
<boost::string_view
, boost::string_view
>>
638 parse_key_value(const boost::string_view
& in_str
)
640 return parse_key_value(in_str
, "=");
643 int parse_time(const char *time_str
, real_time
*time
)
648 if (!parse_rfc2616(time_str
, &tm
) && !parse_iso8601(time_str
, &tm
, &ns
)) {
652 time_t sec
= internal_timegm(&tm
);
653 *time
= utime_t(sec
, ns
).to_real_time();
658 #define TIME_BUF_SIZE 128
660 void rgw_to_iso8601(const real_time
& t
, char *dest
, int buf_size
)
664 char buf
[TIME_BUF_SIZE
];
666 time_t epoch
= ut
.sec();
667 struct tm
*tmp
= gmtime_r(&epoch
, &result
);
671 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T", tmp
) == 0)
674 snprintf(dest
, buf_size
, "%s.%03dZ", buf
, (int)(ut
.usec() / 1000));
677 void rgw_to_iso8601(const real_time
& t
, string
*dest
)
679 char buf
[TIME_BUF_SIZE
];
680 rgw_to_iso8601(t
, buf
, sizeof(buf
));
685 string
rgw_to_asctime(const utime_t
& t
)
693 * calculate the sha1 value of a given msg and key
695 void calc_hmac_sha1(const char *key
, int key_len
,
696 const char *msg
, int msg_len
, char *dest
)
697 /* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
699 HMACSHA1
hmac((const unsigned char *)key
, key_len
);
700 hmac
.Update((const unsigned char *)msg
, msg_len
);
701 hmac
.Final((unsigned char *)dest
);
705 * calculate the sha256 value of a given msg and key
707 void calc_hmac_sha256(const char *key
, int key_len
,
708 const char *msg
, int msg_len
, char *dest
)
710 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
712 HMACSHA256
hmac((const unsigned char *)key
, key_len
);
713 hmac
.Update((const unsigned char *)msg
, msg_len
);
714 hmac
.Final((unsigned char *)hash_sha256
);
716 memcpy(dest
, hash_sha256
, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
);
719 using ceph::crypto::SHA256
;
722 * calculate the sha256 hash value of a given msg
724 sha256_digest_t
calc_hash_sha256(const boost::string_view
& msg
)
726 sha256_digest_t hash
;
729 hasher
.Update(reinterpret_cast<const unsigned char*>(msg
.data()), msg
.size());
730 hasher
.Final(hash
.v
);
735 SHA256
* calc_hash_sha256_open_stream()
740 void calc_hash_sha256_update_stream(SHA256
*hash
, const char *msg
, int len
)
742 hash
->Update((const unsigned char *)msg
, len
);
745 string
calc_hash_sha256_close_stream(SHA256
**phash
)
747 SHA256
*hash
= *phash
;
749 hash
= calc_hash_sha256_open_stream();
751 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
753 hash
->Final((unsigned char *)hash_sha256
);
755 char hex_str
[(CEPH_CRYPTO_SHA256_DIGESTSIZE
* 2) + 1];
756 buf_to_hex((unsigned char *)hash_sha256
, CEPH_CRYPTO_SHA256_DIGESTSIZE
, hex_str
);
761 return std::string(hex_str
);
764 std::string
calc_hash_sha256_restart_stream(SHA256
**phash
)
766 const auto hash
= calc_hash_sha256_close_stream(phash
);
767 *phash
= calc_hash_sha256_open_stream();
774 auto delim_pos
= str
.find('=');
777 if (delim_pos
== string::npos
) {
782 name
= str
.substr(0, delim_pos
);
783 val
= str
.substr(delim_pos
+ 1);
789 int RGWHTTPArgs::parse()
801 int fpos
= str
.find('&', pos
);
806 std::string nameval
= url_decode(str
.substr(pos
, fpos
- pos
), true);
807 NameVal
nv(std::move(nameval
));
808 int ret
= nv
.parse();
810 string
& name
= nv
.get_name();
811 string
& val
= nv
.get_val();
822 void RGWHTTPArgs::append(const string
& name
, const string
& val
)
824 if (name
.compare(0, sizeof(RGW_SYS_PARAM_PREFIX
) - 1, RGW_SYS_PARAM_PREFIX
) == 0) {
825 sys_val_map
[name
] = val
;
830 if ((name
.compare("acl") == 0) ||
831 (name
.compare("cors") == 0) ||
832 (name
.compare("notification") == 0) ||
833 (name
.compare("location") == 0) ||
834 (name
.compare("logging") == 0) ||
835 (name
.compare("usage") == 0) ||
836 (name
.compare("lifecycle") == 0) ||
837 (name
.compare("delete") == 0) ||
838 (name
.compare("uploads") == 0) ||
839 (name
.compare("partNumber") == 0) ||
840 (name
.compare("uploadId") == 0) ||
841 (name
.compare("versionId") == 0) ||
842 (name
.compare("start-date") == 0) ||
843 (name
.compare("end-date") == 0) ||
844 (name
.compare("versions") == 0) ||
845 (name
.compare("versioning") == 0) ||
846 (name
.compare("website") == 0) ||
847 (name
.compare("requestPayment") == 0) ||
848 (name
.compare("torrent") == 0) ||
849 (name
.compare("tagging") == 0) ||
850 (name
.compare("append") == 0) ||
851 (name
.compare("position") == 0) ||
852 (name
.compare("policyStatus") == 0) ||
853 (name
.compare("publicAccessBlock") == 0)) {
854 sub_resources
[name
] = val
;
855 } else if (name
[0] == 'r') { // root of all evil
856 if ((name
.compare("response-content-type") == 0) ||
857 (name
.compare("response-content-language") == 0) ||
858 (name
.compare("response-expires") == 0) ||
859 (name
.compare("response-cache-control") == 0) ||
860 (name
.compare("response-content-disposition") == 0) ||
861 (name
.compare("response-content-encoding") == 0)) {
862 sub_resources
[name
] = val
;
863 has_resp_modifier
= true;
865 } else if ((name
.compare("subuser") == 0) ||
866 (name
.compare("key") == 0) ||
867 (name
.compare("caps") == 0) ||
868 (name
.compare("index") == 0) ||
869 (name
.compare("policy") == 0) ||
870 (name
.compare("quota") == 0) ||
871 (name
.compare("list") == 0) ||
872 (name
.compare("object") == 0) ||
873 (name
.compare("sync") == 0)) {
874 if (!admin_subresource_added
) {
875 sub_resources
[name
] = "";
876 admin_subresource_added
= true;
881 const string
& RGWHTTPArgs::get(const string
& name
, bool *exists
) const
883 auto iter
= val_map
.find(name
);
884 bool e
= (iter
!= std::end(val_map
));
892 boost::optional
<const std::string
&>
893 RGWHTTPArgs::get_optional(const std::string
& name
) const
896 const std::string
& value
= get(name
, &exists
);
904 int RGWHTTPArgs::get_bool(const string
& name
, bool *val
, bool *exists
)
906 map
<string
, string
>::iterator iter
;
907 iter
= val_map
.find(name
);
908 bool e
= (iter
!= val_map
.end());
913 const char *s
= iter
->second
.c_str();
915 if (strcasecmp(s
, "false") == 0) {
917 } else if (strcasecmp(s
, "true") == 0) {
927 int RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool *exists
)
930 return get_bool(s
, val
, exists
);
933 void RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool def_val
)
936 if ((get_bool(name
, val
, &exists
) < 0) ||
942 int RGWHTTPArgs::get_int(const char *name
, int *val
, int def_val
)
946 val_str
= get(name
, &exists
);
954 *val
= (int)strict_strtol(val_str
.c_str(), 10, &err
);
962 string
RGWHTTPArgs::sys_get(const string
& name
, bool * const exists
) const
964 const auto iter
= sys_val_map
.find(name
);
965 const bool e
= (iter
!= sys_val_map
.end());
971 return e
? iter
->second
: string();
974 bool rgw_transport_is_secure(CephContext
*cct
, const RGWEnv
& env
)
976 const auto& m
= env
.get_map();
977 // frontend connected with ssl
978 if (m
.count("SERVER_PORT_SECURE")) {
981 // ignore proxy headers unless explicitly enabled
982 if (!cct
->_conf
->rgw_trust_forwarded_https
) {
985 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
986 // Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>
987 auto i
= m
.find("HTTP_FORWARDED");
988 if (i
!= m
.end() && i
->second
.find("proto=https") != std::string::npos
) {
991 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
992 i
= m
.find("HTTP_X_FORWARDED_PROTO");
993 if (i
!= m
.end() && i
->second
== "https") {
1002 struct perm_state_from_req_state
: public perm_state_base
{
1003 req_state
* const s
;
1004 perm_state_from_req_state(req_state
* const _s
) : perm_state_base(_s
->cct
,
1006 _s
->auth
.identity
.get(),
1009 _s
->defer_to_bucket_acls
,
1010 _s
->bucket_access_conf
),
1012 std::optional
<bool> get_request_payer() const override
{
1013 const char *request_payer
= s
->info
.env
->get("HTTP_X_AMZ_REQUEST_PAYER");
1014 if (!request_payer
) {
1016 request_payer
= s
->info
.args
.get("x-amz-request-payer", &exists
).c_str();
1022 if (strcasecmp(request_payer
, "requester") == 0) {
1026 return std::nullopt
;
1029 const char *get_referer() const override
{
1030 return s
->info
.env
->get("HTTP_REFERER");
1034 Effect
eval_or_pass(const boost::optional
<Policy
>& policy
,
1035 const rgw::IAM::Environment
& env
,
1036 boost::optional
<const rgw::auth::Identity
&> id
,
1040 return Effect::Pass
;
1042 return policy
->eval(env
, id
, op
, arn
);
1047 Effect
eval_user_policies(const vector
<Policy
>& user_policies
,
1048 const rgw::IAM::Environment
& env
,
1049 boost::optional
<const rgw::auth::Identity
&> id
,
1052 auto usr_policy_res
= Effect::Pass
, prev_res
= Effect::Pass
;
1053 for (auto& user_policy
: user_policies
) {
1054 if (usr_policy_res
= eval_or_pass(user_policy
, env
, id
, op
, arn
); usr_policy_res
== Effect::Deny
)
1055 return usr_policy_res
;
1056 else if (usr_policy_res
== Effect::Allow
)
1057 prev_res
= Effect::Allow
;
1058 else if (usr_policy_res
== Effect::Pass
&& prev_res
== Effect::Allow
)
1059 usr_policy_res
= Effect::Allow
;
1061 return usr_policy_res
;
1064 bool verify_user_permission(const DoutPrefixProvider
* dpp
,
1065 perm_state_base
* const s
,
1066 RGWAccessControlPolicy
* const user_acl
,
1067 const vector
<rgw::IAM::Policy
>& user_policies
,
1068 const rgw::ARN
& res
,
1071 auto usr_policy_res
= eval_user_policies(user_policies
, s
->env
, boost::none
, op
, res
);
1072 if (usr_policy_res
== Effect::Deny
) {
1076 if (usr_policy_res
== Effect::Allow
) {
1080 if (op
== rgw::IAM::s3CreateBucket
|| op
== rgw::IAM::s3ListAllMyBuckets
) {
1081 auto perm
= op_to_perm(op
);
1083 return verify_user_permission_no_policy(dpp
, s
, user_acl
, perm
);
1089 bool verify_user_permission_no_policy(const DoutPrefixProvider
* dpp
,
1090 struct perm_state_base
* const s
,
1091 RGWAccessControlPolicy
* const user_acl
,
1094 if (s
->identity
->get_identity_type() == TYPE_ROLE
)
1097 /* S3 doesn't support account ACLs. */
1101 if ((perm
& (int)s
->perm_mask
) != perm
)
1104 return user_acl
->verify_permission(dpp
, *s
->identity
, perm
, perm
);
1107 bool verify_user_permission(const DoutPrefixProvider
* dpp
,
1108 struct req_state
* const s
,
1109 const rgw::ARN
& res
,
1112 perm_state_from_req_state
ps(s
);
1113 return verify_user_permission(dpp
, &ps
, s
->user_acl
.get(), s
->iam_user_policies
, res
, op
);
1116 bool verify_user_permission_no_policy(const DoutPrefixProvider
* dpp
,
1117 struct req_state
* const s
,
1120 perm_state_from_req_state
ps(s
);
1121 return verify_user_permission_no_policy(dpp
, &ps
, s
->user_acl
.get(), perm
);
1124 bool verify_requester_payer_permission(struct perm_state_base
*s
)
1126 if (!s
->bucket_info
.requester_pays
)
1129 if (s
->identity
->is_owner_of(s
->bucket_info
.owner
))
1132 if (s
->identity
->is_anonymous()) {
1136 auto request_payer
= s
->get_request_payer();
1137 if (request_payer
) {
1138 return *request_payer
;
1144 bool verify_bucket_permission(const DoutPrefixProvider
* dpp
,
1145 struct perm_state_base
* const s
,
1146 const rgw_bucket
& bucket
,
1147 RGWAccessControlPolicy
* const user_acl
,
1148 RGWAccessControlPolicy
* const bucket_acl
,
1149 const boost::optional
<Policy
>& bucket_policy
,
1150 const vector
<Policy
>& user_policies
,
1153 if (!verify_requester_payer_permission(s
))
1156 auto usr_policy_res
= eval_user_policies(user_policies
, s
->env
, boost::none
, op
, ARN(bucket
));
1157 if (usr_policy_res
== Effect::Deny
)
1160 auto r
= eval_or_pass(bucket_policy
, s
->env
, *s
->identity
,
1162 if (r
== Effect::Allow
)
1163 // It looks like S3 ACLs only GRANT permissions rather than
1164 // denying them, so this should be safe.
1166 else if (r
== Effect::Deny
)
1168 else if (usr_policy_res
== Effect::Allow
) // r is Effect::Pass at this point
1171 const auto perm
= op_to_perm(op
);
1173 return verify_bucket_permission_no_policy(dpp
, s
, user_acl
, bucket_acl
, perm
);
1176 bool verify_bucket_permission(const DoutPrefixProvider
* dpp
,
1177 struct req_state
* const s
,
1178 const rgw_bucket
& bucket
,
1179 RGWAccessControlPolicy
* const user_acl
,
1180 RGWAccessControlPolicy
* const bucket_acl
,
1181 const boost::optional
<Policy
>& bucket_policy
,
1182 const vector
<Policy
>& user_policies
,
1185 perm_state_from_req_state
ps(s
);
1186 return verify_bucket_permission(dpp
, &ps
, bucket
,
1187 user_acl
, bucket_acl
,
1188 bucket_policy
, user_policies
,
1192 bool verify_bucket_permission_no_policy(const DoutPrefixProvider
* dpp
, struct perm_state_base
* const s
,
1193 RGWAccessControlPolicy
* const user_acl
,
1194 RGWAccessControlPolicy
* const bucket_acl
,
1200 if ((perm
& (int)s
->perm_mask
) != perm
)
1203 if (bucket_acl
->verify_permission(dpp
, *s
->identity
, perm
, perm
,
1205 s
->bucket_access_conf
&&
1206 s
->bucket_access_conf
->ignore_public_acls()))
1212 return user_acl
->verify_permission(dpp
, *s
->identity
, perm
, perm
);
1215 bool verify_bucket_permission_no_policy(const DoutPrefixProvider
* dpp
, struct req_state
* const s
,
1216 RGWAccessControlPolicy
* const user_acl
,
1217 RGWAccessControlPolicy
* const bucket_acl
,
1220 perm_state_from_req_state
ps(s
);
1221 return verify_bucket_permission_no_policy(dpp
,
1228 bool verify_bucket_permission_no_policy(const DoutPrefixProvider
* dpp
, struct req_state
* const s
, const int perm
)
1230 perm_state_from_req_state
ps(s
);
1232 if (!verify_requester_payer_permission(&ps
))
1235 return verify_bucket_permission_no_policy(dpp
,
1238 s
->bucket_acl
.get(),
1242 bool verify_bucket_permission(const DoutPrefixProvider
* dpp
, struct req_state
* const s
, const uint64_t op
)
1244 perm_state_from_req_state
ps(s
);
1246 return verify_bucket_permission(dpp
,
1250 s
->bucket_acl
.get(),
1252 s
->iam_user_policies
,
1256 // Authorize anyone permitted by the policy and the bucket owner
1257 // unless explicitly denied by the policy.
1259 int verify_bucket_owner_or_policy(struct req_state
* const s
,
1262 auto e
= eval_or_pass(s
->iam_policy
,
1263 s
->env
, *s
->auth
.identity
,
1264 op
, ARN(s
->bucket
));
1265 if (e
== Effect::Allow
||
1266 (e
== Effect::Pass
&&
1267 s
->auth
.identity
->is_owner_of(s
->bucket_owner
.get_id()))) {
1275 static inline bool check_deferred_bucket_perms(const DoutPrefixProvider
* dpp
,
1276 struct perm_state_base
* const s
,
1277 const rgw_bucket
& bucket
,
1278 RGWAccessControlPolicy
* const user_acl
,
1279 RGWAccessControlPolicy
* const bucket_acl
,
1280 const boost::optional
<Policy
>& bucket_policy
,
1281 const vector
<Policy
>& user_policies
,
1282 const uint8_t deferred_check
,
1285 return (s
->defer_to_bucket_acls
== deferred_check \
1286 && verify_bucket_permission(dpp
, s
, bucket
, user_acl
, bucket_acl
, bucket_policy
, user_policies
,op
));
1289 static inline bool check_deferred_bucket_only_acl(const DoutPrefixProvider
* dpp
,
1290 struct perm_state_base
* const s
,
1291 RGWAccessControlPolicy
* const user_acl
,
1292 RGWAccessControlPolicy
* const bucket_acl
,
1293 const uint8_t deferred_check
,
1296 return (s
->defer_to_bucket_acls
== deferred_check \
1297 && verify_bucket_permission_no_policy(dpp
, s
, user_acl
, bucket_acl
, perm
));
1300 bool verify_object_permission(const DoutPrefixProvider
* dpp
, struct perm_state_base
* const s
,
1302 RGWAccessControlPolicy
* const user_acl
,
1303 RGWAccessControlPolicy
* const bucket_acl
,
1304 RGWAccessControlPolicy
* const object_acl
,
1305 const boost::optional
<Policy
>& bucket_policy
,
1306 const vector
<Policy
>& user_policies
,
1309 if (!verify_requester_payer_permission(s
))
1312 auto usr_policy_res
= eval_user_policies(user_policies
, s
->env
, boost::none
, op
, ARN(obj
));
1313 if (usr_policy_res
== Effect::Deny
)
1316 auto r
= eval_or_pass(bucket_policy
, s
->env
, *s
->identity
, op
, ARN(obj
));
1317 if (r
== Effect::Allow
)
1318 // It looks like S3 ACLs only GRANT permissions rather than
1319 // denying them, so this should be safe.
1321 else if (r
== Effect::Deny
)
1323 else if (usr_policy_res
== Effect::Allow
)
1326 const auto perm
= op_to_perm(op
);
1328 if (check_deferred_bucket_perms(dpp
, s
, obj
.bucket
, user_acl
, bucket_acl
, bucket_policy
,
1329 user_policies
, RGW_DEFER_TO_BUCKET_ACLS_RECURSE
, op
) ||
1330 check_deferred_bucket_perms(dpp
, s
, obj
.bucket
, user_acl
, bucket_acl
, bucket_policy
,
1331 user_policies
, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL
, rgw::IAM::s3All
)) {
1339 bool ret
= object_acl
->verify_permission(dpp
, *s
->identity
, s
->perm_mask
, perm
,
1340 nullptr, /* http_referrer */
1341 s
->bucket_access_conf
&&
1342 s
->bucket_access_conf
->ignore_public_acls());
1347 if (!s
->cct
->_conf
->rgw_enforce_swift_acls
)
1350 if ((perm
& (int)s
->perm_mask
) != perm
)
1354 if (perm
& (RGW_PERM_READ
| RGW_PERM_READ_ACP
))
1355 swift_perm
|= RGW_PERM_READ_OBJS
;
1356 if (perm
& RGW_PERM_WRITE
)
1357 swift_perm
|= RGW_PERM_WRITE_OBJS
;
1362 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1363 otherwise the mask might not cover the swift permissions bits */
1364 if (bucket_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
,
1371 return user_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
);
1374 bool verify_object_permission(const DoutPrefixProvider
* dpp
, struct req_state
* const s
,
1376 RGWAccessControlPolicy
* const user_acl
,
1377 RGWAccessControlPolicy
* const bucket_acl
,
1378 RGWAccessControlPolicy
* const object_acl
,
1379 const boost::optional
<Policy
>& bucket_policy
,
1380 const vector
<Policy
>& user_policies
,
1383 perm_state_from_req_state
ps(s
);
1384 return verify_object_permission(dpp
, &ps
, obj
,
1385 user_acl
, bucket_acl
,
1386 object_acl
, bucket_policy
,
1390 bool verify_object_permission_no_policy(const DoutPrefixProvider
* dpp
,
1391 struct perm_state_base
* const s
,
1392 RGWAccessControlPolicy
* const user_acl
,
1393 RGWAccessControlPolicy
* const bucket_acl
,
1394 RGWAccessControlPolicy
* const object_acl
,
1397 if (check_deferred_bucket_only_acl(dpp
, s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_RECURSE
, perm
) ||
1398 check_deferred_bucket_only_acl(dpp
, s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL
, RGW_PERM_FULL_CONTROL
)) {
1406 bool ret
= object_acl
->verify_permission(dpp
, *s
->identity
, s
->perm_mask
, perm
,
1407 nullptr, /* http referrer */
1408 s
->bucket_access_conf
&&
1409 s
->bucket_access_conf
->ignore_public_acls());
1414 if (!s
->cct
->_conf
->rgw_enforce_swift_acls
)
1417 if ((perm
& (int)s
->perm_mask
) != perm
)
1421 if (perm
& (RGW_PERM_READ
| RGW_PERM_READ_ACP
))
1422 swift_perm
|= RGW_PERM_READ_OBJS
;
1423 if (perm
& RGW_PERM_WRITE
)
1424 swift_perm
|= RGW_PERM_WRITE_OBJS
;
1429 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1430 otherwise the mask might not cover the swift permissions bits */
1431 if (bucket_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
,
1438 return user_acl
->verify_permission(dpp
, *s
->identity
, swift_perm
, swift_perm
);
1441 bool verify_object_permission_no_policy(const DoutPrefixProvider
* dpp
, struct req_state
*s
, int perm
)
1443 perm_state_from_req_state
ps(s
);
1445 if (!verify_requester_payer_permission(&ps
))
1448 return verify_object_permission_no_policy(dpp
,
1451 s
->bucket_acl
.get(),
1452 s
->object_acl
.get(),
1456 bool verify_object_permission(const DoutPrefixProvider
* dpp
, struct req_state
*s
, uint64_t op
)
1458 perm_state_from_req_state
ps(s
);
1460 return verify_object_permission(dpp
,
1462 rgw_obj(s
->bucket
, s
->object
),
1464 s
->bucket_acl
.get(),
1465 s
->object_acl
.get(),
1467 s
->iam_user_policies
,
1477 // FIPS zeroization audit 20191115: this memset is not security related.
1478 memset(table
, -1, sizeof(table
));
1480 for (i
= '0'; i
<='9'; i
++)
1482 for (i
= 'A'; i
<='F'; i
++)
1483 table
[i
] = i
- 'A' + 0xa;
1484 for (i
= 'a'; i
<='f'; i
++)
1485 table
[i
] = i
- 'a' + 0xa;
1488 char to_num(char c
) {
1489 return table
[(int)c
];
1493 static char hex_to_num(char c
)
1495 static HexTable hex_table
;
1496 return hex_table
.to_num(c
);
1499 std::string
url_decode(const boost::string_view
& src_str
, bool in_query
)
1501 std::string dest_str
;
1502 dest_str
.reserve(src_str
.length() + 1);
1504 for (auto src
= std::begin(src_str
); src
!= std::end(src_str
); ++src
) {
1506 if (!in_query
|| *src
!= '+') {
1510 dest_str
.push_back(*src
);
1512 dest_str
.push_back(' ');
1515 /* 3 == strlen("%%XX") */
1516 if (std::distance(src
, std::end(src_str
)) < 3) {
1521 const char c1
= hex_to_num(*src
++);
1522 const char c2
= hex_to_num(*src
);
1523 if (c1
< 0 || c2
< 0) {
1524 return std::string();
1526 dest_str
.push_back(c1
<< 4 | c2
);
1534 void rgw_uri_escape_char(char c
, string
& dst
)
1537 snprintf(buf
, sizeof(buf
), "%%%.2X", (int)(unsigned char)c
);
1541 static bool char_needs_url_encoding(char c
)
1543 if (c
<= 0x20 || c
>= 0x7f)
1573 void url_encode(const string
& src
, string
& dst
, bool encode_slash
)
1575 const char *p
= src
.c_str();
1576 for (unsigned i
= 0; i
< src
.size(); i
++, p
++) {
1577 if ((!encode_slash
&& *p
== 0x2F) || !char_needs_url_encoding(*p
)) {
1580 rgw_uri_escape_char(*p
, dst
);
1585 std::string
url_encode(const std::string
& src
, bool encode_slash
)
1588 url_encode(src
, dst
, encode_slash
);
1593 string
rgw_trim_whitespace(const string
& src
)
1600 for (; start
!= (int)src
.size(); start
++) {
1601 if (!isspace(src
[start
]))
1605 int end
= src
.size() - 1;
1610 for (; end
> start
; end
--) {
1611 if (!isspace(src
[end
]))
1615 return src
.substr(start
, end
- start
+ 1);
1618 boost::string_view
rgw_trim_whitespace(const boost::string_view
& src
)
1620 boost::string_view res
= src
;
1622 while (res
.size() > 0 && std::isspace(res
.front())) {
1623 res
.remove_prefix(1);
1625 while (res
.size() > 0 && std::isspace(res
.back())) {
1626 res
.remove_suffix(1);
1631 string
rgw_trim_quotes(const string
& val
)
1633 string s
= rgw_trim_whitespace(val
);
1638 int end
= s
.size() - 1;
1639 int quotes_count
= 0;
1641 if (s
[start
] == '"') {
1645 if (s
[end
] == '"') {
1649 if (quotes_count
== 2) {
1650 return s
.substr(start
, end
- start
+ 1);
1655 static struct rgw_name_to_flag cap_names
[] = { {"*", RGW_CAP_ALL
},
1656 {"read", RGW_CAP_READ
},
1657 {"write", RGW_CAP_WRITE
},
1660 int RGWUserCaps::parse_cap_perm(const string
& str
, uint32_t *perm
)
1662 return rgw_parse_list_of_flags(cap_names
, str
, perm
);
1665 int RGWUserCaps::get_cap(const string
& cap
, string
& type
, uint32_t *pperm
)
1667 int pos
= cap
.find('=');
1669 type
= rgw_trim_whitespace(cap
.substr(0, pos
));
1672 if (!is_valid_cap_type(type
))
1673 return -ERR_INVALID_CAP
;
1677 if (pos
< (int)cap
.size() - 1) {
1678 cap_perm
= cap
.substr(pos
+ 1);
1679 int r
= RGWUserCaps::parse_cap_perm(cap_perm
, &perm
);
1689 int RGWUserCaps::add_cap(const string
& cap
)
1694 int r
= get_cap(cap
, type
, &perm
);
1703 int RGWUserCaps::remove_cap(const string
& cap
)
1708 int r
= get_cap(cap
, type
, &perm
);
1712 map
<string
, uint32_t>::iterator iter
= caps
.find(type
);
1713 if (iter
== caps
.end())
1716 uint32_t& old_perm
= iter
->second
;
1724 int RGWUserCaps::add_from_string(const string
& str
)
1728 auto end
= str
.find(';', start
);
1729 if (end
== string::npos
)
1732 int r
= add_cap(str
.substr(start
, end
- start
));
1737 } while (start
< (int)str
.size());
1742 int RGWUserCaps::remove_from_string(const string
& str
)
1746 auto end
= str
.find(';', start
);
1747 if (end
== string::npos
)
1750 int r
= remove_cap(str
.substr(start
, end
- start
));
1755 } while (start
< (int)str
.size());
1760 void RGWUserCaps::dump(Formatter
*f
) const
1765 void RGWUserCaps::dump(Formatter
*f
, const char *name
) const
1767 f
->open_array_section(name
);
1768 map
<string
, uint32_t>::const_iterator iter
;
1769 for (iter
= caps
.begin(); iter
!= caps
.end(); ++iter
)
1771 f
->open_object_section("cap");
1772 f
->dump_string("type", iter
->first
);
1773 uint32_t perm
= iter
->second
;
1775 for (int i
=0; cap_names
[i
].type_name
; i
++) {
1776 if ((perm
& cap_names
[i
].flag
) == cap_names
[i
].flag
) {
1777 if (perm_str
.size())
1778 perm_str
.append(", ");
1780 perm_str
.append(cap_names
[i
].type_name
);
1781 perm
&= ~cap_names
[i
].flag
;
1784 if (perm_str
.empty())
1785 perm_str
= "<none>";
1787 f
->dump_string("perm", perm_str
);
1798 void decode_json(JSONObj
*obj
) {
1799 JSONDecoder::decode_json("type", type
, obj
);
1801 JSONDecoder::decode_json("perm", perm_str
, obj
);
1802 if (RGWUserCaps::parse_cap_perm(perm_str
, &perm
) < 0) {
1803 throw JSONDecoder::err("failed to parse permissions");
1808 void RGWUserCaps::decode_json(JSONObj
*obj
)
1810 list
<RGWUserCap
> caps_list
;
1811 decode_json_obj(caps_list
, obj
);
1813 list
<RGWUserCap
>::iterator iter
;
1814 for (iter
= caps_list
.begin(); iter
!= caps_list
.end(); ++iter
) {
1815 RGWUserCap
& cap
= *iter
;
1816 caps
[cap
.type
] = cap
.perm
;
1820 int RGWUserCaps::check_cap(const string
& cap
, uint32_t perm
) const
1822 auto iter
= caps
.find(cap
);
1824 if ((iter
== caps
.end()) ||
1825 (iter
->second
& perm
) != perm
) {
1832 bool RGWUserCaps::is_valid_cap_type(const string
& tp
)
1834 static const char *cap_type
[] = { "user",
1847 for (unsigned int i
= 0; i
< sizeof(cap_type
) / sizeof(char *); ++i
) {
1848 if (tp
.compare(cap_type
[i
]) == 0) {
1856 void rgw_pool::from_str(const string
& s
)
1858 size_t pos
= rgw_unescape_str(s
, 0, '\\', ':', &name
);
1859 if (pos
!= string::npos
) {
1860 pos
= rgw_unescape_str(s
, pos
, '\\', ':', &ns
);
1861 /* ignore return; if pos != string::npos it means that we had a colon
1862 * in the middle of ns that wasn't escaped, we're going to stop there
1867 string
rgw_pool::to_str() const
1870 rgw_escape_str(name
, '\\', ':', &esc_name
);
1875 rgw_escape_str(ns
, '\\', ':', &esc_ns
);
1876 return esc_name
+ ":" + esc_ns
;
1879 void rgw_raw_obj::decode_from_rgw_obj(bufferlist::const_iterator
& bl
)
1883 decode(old_obj
, bl
);
1885 get_obj_bucket_and_oid_loc(old_obj
, oid
, loc
);
1886 pool
= old_obj
.get_explicit_data_pool();
1889 static struct rgw_name_to_flag op_type_mapping
[] = { {"*", RGW_OP_TYPE_ALL
},
1890 {"read", RGW_OP_TYPE_READ
},
1891 {"write", RGW_OP_TYPE_WRITE
},
1892 {"delete", RGW_OP_TYPE_DELETE
},
1896 int rgw_parse_op_type_list(const string
& str
, uint32_t *perm
)
1898 return rgw_parse_list_of_flags(op_type_mapping
, str
, perm
);
1901 bool match_policy(boost::string_view pattern
, boost::string_view input
,
1904 const uint32_t flag2
= flag
& (MATCH_POLICY_ACTION
|MATCH_POLICY_ARN
) ?
1905 MATCH_CASE_INSENSITIVE
: 0;
1906 const bool colonblocks
= !(flag
& (MATCH_POLICY_RESOURCE
|
1907 MATCH_POLICY_STRING
));
1909 const auto npos
= boost::string_view::npos
;
1910 boost::string_view::size_type last_pos_input
= 0, last_pos_pattern
= 0;
1912 auto cur_pos_input
= colonblocks
? input
.find(":", last_pos_input
) : npos
;
1913 auto cur_pos_pattern
=
1914 colonblocks
? pattern
.find(":", last_pos_pattern
) : npos
;
1916 auto substr_input
= input
.substr(last_pos_input
, cur_pos_input
);
1917 auto substr_pattern
= pattern
.substr(last_pos_pattern
, cur_pos_pattern
);
1919 if (!match_wildcards(substr_pattern
, substr_input
, flag2
))
1922 if (cur_pos_pattern
== npos
)
1923 return cur_pos_input
== npos
;
1924 if (cur_pos_input
== npos
)
1927 last_pos_pattern
= cur_pos_pattern
+ 1;
1928 last_pos_input
= cur_pos_input
+ 1;
1933 * make attrs look-like-this
1934 * converts underscores to dashes
1936 string
lowercase_dash_http_attr(const string
& orig
)
1938 const char *s
= orig
.c_str();
1939 char buf
[orig
.size() + 1];
1940 buf
[orig
.size()] = '\0';
1942 for (size_t i
= 0; i
< orig
.size(); ++i
, ++s
) {
1948 buf
[i
] = tolower(*s
);
1955 * make attrs Look-Like-This
1956 * converts underscores to dashes
1958 string
camelcase_dash_http_attr(const string
& orig
)
1960 const char *s
= orig
.c_str();
1961 char buf
[orig
.size() + 1];
1962 buf
[orig
.size()] = '\0';
1964 bool last_sep
= true;
1966 for (size_t i
= 0; i
< orig
.size(); ++i
, ++s
) {
1975 buf
[i
] = toupper(*s
);
1977 buf
[i
] = tolower(*s
);
1985 RGWBucketInfo::RGWBucketInfo()
1989 RGWBucketInfo::~RGWBucketInfo()
1993 void RGWBucketInfo::encode(bufferlist
& bl
) const {
1994 ENCODE_START(21, 4, bl
);
1996 encode(owner
.id
, bl
);
1998 encode(zonegroup
, bl
);
1999 uint64_t ct
= real_clock::to_time_t(creation_time
);
2001 encode(placement_rule
, bl
);
2002 encode(has_instance_obj
, bl
);
2004 encode(num_shards
, bl
);
2005 encode(bucket_index_shard_hash_type
, bl
);
2006 encode(requester_pays
, bl
);
2007 encode(owner
.tenant
, bl
);
2008 encode(has_website
, bl
);
2010 encode(website_conf
, bl
);
2012 encode((uint32_t)index_type
, bl
);
2013 encode(swift_versioning
, bl
);
2014 if (swift_versioning
) {
2015 encode(swift_ver_location
, bl
);
2017 encode(creation_time
, bl
);
2018 encode(mdsearch_config
, bl
);
2019 encode(reshard_status
, bl
);
2020 encode(new_bucket_instance_id
, bl
);
2021 if (obj_lock_enabled()) {
2022 encode(obj_lock
, bl
);
2024 bool has_sync_policy
= !empty_sync_policy();
2025 encode(has_sync_policy
, bl
);
2026 if (has_sync_policy
) {
2027 encode(*sync_policy
, bl
);
2032 void RGWBucketInfo::decode(bufferlist::const_iterator
& bl
) {
2033 DECODE_START_LEGACY_COMPAT_LEN_32(21, 4, 4, bl
);
2035 if (struct_v
>= 2) {
2043 decode(zonegroup
, bl
);
2044 if (struct_v
>= 6) {
2048 creation_time
= ceph::real_clock::from_time_t((time_t)ct
);
2051 decode(placement_rule
, bl
);
2053 decode(has_instance_obj
, bl
);
2057 decode(num_shards
, bl
);
2059 decode(bucket_index_shard_hash_type
, bl
);
2061 decode(requester_pays
, bl
);
2063 decode(owner
.tenant
, bl
);
2064 if (struct_v
>= 14) {
2065 decode(has_website
, bl
);
2067 decode(website_conf
, bl
);
2069 website_conf
= RGWBucketWebsiteConf();
2072 if (struct_v
>= 15) {
2075 index_type
= (RGWBucketIndexType
)it
;
2077 index_type
= RGWBIType_Normal
;
2079 swift_versioning
= false;
2080 swift_ver_location
.clear();
2081 if (struct_v
>= 16) {
2082 decode(swift_versioning
, bl
);
2083 if (swift_versioning
) {
2084 decode(swift_ver_location
, bl
);
2087 if (struct_v
>= 17) {
2088 decode(creation_time
, bl
);
2090 if (struct_v
>= 18) {
2091 decode(mdsearch_config
, bl
);
2093 if (struct_v
>= 19) {
2094 decode(reshard_status
, bl
);
2095 decode(new_bucket_instance_id
, bl
);
2097 if (struct_v
>= 20 && obj_lock_enabled()) {
2098 decode(obj_lock
, bl
);
2100 if (struct_v
>= 21) {
2101 decode(sync_policy
, bl
);
2107 void RGWBucketInfo::set_sync_policy(rgw_sync_policy_info
&& policy
)
2109 sync_policy
= std::move(policy
);
2112 bool RGWBucketInfo::empty_sync_policy() const
2118 return sync_policy
->empty();