1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
8 #include <boost/tokenizer.hpp>
9 #include <boost/algorithm/string.hpp>
11 #include "json_spirit/json_spirit.h"
12 #include "common/ceph_json.h"
14 #include "rgw_common.h"
16 #include "rgw_string.h"
17 #include "rgw_rados.h"
19 #include "common/ceph_crypto.h"
20 #include "common/armor.h"
21 #include "common/errno.h"
22 #include "common/Clock.h"
23 #include "common/Formatter.h"
24 #include "common/perf_counters.h"
25 #include "common/strtol.h"
26 #include "include/str_list.h"
27 #include "auth/Crypto.h"
28 #include "rgw_crypt_sanitize.h"
32 #define dout_context g_ceph_context
33 #define dout_subsys ceph_subsys_rgw
35 #define POLICY_ACTION 0x01
36 #define POLICY_RESOURCE 0x02
37 #define POLICY_ARN 0x04
38 #define POLICY_STRING 0x08
40 PerfCounters
*perfcounter
= NULL
;
42 const uint32_t RGWBucketInfo::NUM_SHARDS_BLIND_BUCKET(UINT32_MAX
);
44 int rgw_perf_start(CephContext
*cct
)
46 PerfCountersBuilder
plb(cct
, cct
->_conf
->name
.to_str(), l_rgw_first
, l_rgw_last
);
48 plb
.add_u64_counter(l_rgw_req
, "req", "Requests");
49 plb
.add_u64_counter(l_rgw_failed_req
, "failed_req", "Aborted requests");
51 plb
.add_u64_counter(l_rgw_get
, "get", "Gets");
52 plb
.add_u64_counter(l_rgw_get_b
, "get_b", "Size of gets");
53 plb
.add_time_avg(l_rgw_get_lat
, "get_initial_lat", "Get latency");
54 plb
.add_u64_counter(l_rgw_put
, "put", "Puts");
55 plb
.add_u64_counter(l_rgw_put_b
, "put_b", "Size of puts");
56 plb
.add_time_avg(l_rgw_put_lat
, "put_initial_lat", "Put latency");
58 plb
.add_u64(l_rgw_qlen
, "qlen", "Queue length");
59 plb
.add_u64(l_rgw_qactive
, "qactive", "Active requests queue");
61 plb
.add_u64_counter(l_rgw_cache_hit
, "cache_hit", "Cache hits");
62 plb
.add_u64_counter(l_rgw_cache_miss
, "cache_miss", "Cache miss");
64 plb
.add_u64_counter(l_rgw_keystone_token_cache_hit
, "keystone_token_cache_hit", "Keystone token cache hits");
65 plb
.add_u64_counter(l_rgw_keystone_token_cache_miss
, "keystone_token_cache_miss", "Keystone token cache miss");
67 perfcounter
= plb
.create_perf_counters();
68 cct
->get_perfcounters_collection()->add(perfcounter
);
72 void rgw_perf_stop(CephContext
*cct
)
75 cct
->get_perfcounters_collection()->remove(perfcounter
);
79 using namespace ceph::crypto
;
88 rgw_err(int http
, const std::string
& s3
)
89 : http_ret(http
), ret(0), s3_code(s3
)
104 return (http_ret
== 200);
110 return !(http_ret
>= 200 && http_ret
<= 399);
113 // The requestURI transferred from the frontend can be abs_path or absoluteURI
114 // If it is absoluteURI, we should adjust it to abs_path for the following
115 // S3 authorization and some other processes depending on the requestURI
116 // The absoluteURI can start with "http://", "https://", "ws://" or "wss://"
117 static string
get_abs_path(const string
& request_uri
) {
118 const static string ABS_PREFIXS
[] = {"http://", "https://", "ws://", "wss://"};
120 for (int i
= 0; i
< 4; ++i
) {
121 if (boost::algorithm::starts_with(request_uri
, ABS_PREFIXS
[i
])) {
126 if (!isAbs
) { // it is not a valid absolute uri
129 size_t beg_pos
= request_uri
.find("://") + 3;
130 size_t len
= request_uri
.size();
131 beg_pos
= request_uri
.find('/', beg_pos
);
132 if (beg_pos
== string::npos
) return request_uri
;
133 return request_uri
.substr(beg_pos
, len
- beg_pos
);
136 req_info::req_info(CephContext
*cct
, class RGWEnv
*e
) : env(e
) {
137 method
= env
->get("REQUEST_METHOD", "");
138 script_uri
= env
->get("SCRIPT_URI", cct
->_conf
->rgw_script_uri
.c_str());
139 request_uri
= env
->get("REQUEST_URI", cct
->_conf
->rgw_request_uri
.c_str());
140 if (request_uri
[0] != '/') {
141 request_uri
= get_abs_path(request_uri
);
143 int pos
= request_uri
.find('?');
145 request_params
= request_uri
.substr(pos
+ 1);
146 request_uri
= request_uri
.substr(0, pos
);
148 request_params
= env
->get("QUERY_STRING", "");
150 host
= env
->get("HTTP_HOST", "");
152 // strip off any trailing :port from host (added by CrossFTP and maybe others)
153 size_t colon_offset
= host
.find_last_of(':');
154 if (colon_offset
!= string::npos
) {
155 bool all_digits
= true;
156 for (unsigned i
= colon_offset
+ 1; i
< host
.size(); ++i
) {
157 if (!isdigit(host
[i
])) {
163 host
.resize(colon_offset
);
168 void req_info::rebuild_from(req_info
& src
)
171 script_uri
= src
.script_uri
;
173 if (src
.effective_uri
.empty()) {
174 request_uri
= src
.request_uri
;
176 request_uri
= src
.effective_uri
;
178 effective_uri
.clear();
181 x_meta_map
= src
.x_meta_map
;
182 x_meta_map
.erase("x-amz-date");
186 req_state::req_state(CephContext
* _cct
, RGWEnv
* e
, RGWUserInfo
* u
)
187 : cct(_cct
), cio(NULL
), op(OP_UNKNOWN
), user(u
), has_acl_header(false),
190 enable_ops_log
= e
->conf
.enable_ops_log
;
191 enable_usage_log
= e
->conf
.enable_usage_log
;
192 defer_to_bucket_acls
= e
->conf
.defer_to_bucket_acls
;
193 content_started
= false;
199 aws4_auth_needs_complete
= false;
200 aws4_auth_streaming_mode
= false;
202 header_ended
= false;
206 system_request
= false;
208 time
= ceph_clock_now();
210 bucket_instance_shard_id
= -1;
212 bucket_exists
= false;
213 has_bad_meta
= false;
216 local_source
= false;
221 req_state::~req_state() {
232 #define STR_LEN_ENTRY(s) { s, sizeof(s) - 1 }
234 struct str_len meta_prefixes
[] = { STR_LEN_ENTRY("HTTP_X_AMZ"),
235 STR_LEN_ENTRY("HTTP_X_GOOG"),
236 STR_LEN_ENTRY("HTTP_X_DHO"),
237 STR_LEN_ENTRY("HTTP_X_RGW"),
238 STR_LEN_ENTRY("HTTP_X_OBJECT"),
239 STR_LEN_ENTRY("HTTP_X_CONTAINER"),
240 STR_LEN_ENTRY("HTTP_X_ACCOUNT"),
244 void req_info::init_meta_info(bool *found_bad_meta
)
248 map
<string
, string
, ltstr_nocase
>& m
= env
->get_map();
249 map
<string
, string
, ltstr_nocase
>::iterator iter
;
250 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
252 const string
& header_name
= iter
->first
;
253 const string
& val
= iter
->second
;
254 for (int prefix_num
= 0; (prefix
= meta_prefixes
[prefix_num
].str
) != NULL
; prefix_num
++) {
255 int len
= meta_prefixes
[prefix_num
].len
;
256 const char *p
= header_name
.c_str();
257 if (strncmp(p
, prefix
, len
) == 0) {
258 dout(10) << "meta>> " << p
<< dendl
;
259 const char *name
= p
+len
; /* skip the prefix */
260 int name_len
= header_name
.size() - len
;
262 if (found_bad_meta
&& strncmp(name
, "_META_", name_len
) == 0)
263 *found_bad_meta
= true;
265 char name_low
[meta_prefixes
[0].len
+ name_len
+ 1];
266 snprintf(name_low
, meta_prefixes
[0].len
- 5 + name_len
+ 1, "%s%s", meta_prefixes
[0].str
+ 5 /* skip HTTP_ */, name
); // normalize meta prefix
268 for (j
= 0; name_low
[j
]; j
++) {
269 if (name_low
[j
] != '_')
270 name_low
[j
] = tolower(name_low
[j
]);
276 map
<string
, string
>::iterator iter
;
277 iter
= x_meta_map
.find(name_low
);
278 if (iter
!= x_meta_map
.end()) {
279 string old
= iter
->second
;
280 int pos
= old
.find_last_not_of(" \t"); /* get rid of any whitespaces after the value */
281 old
= old
.substr(0, pos
+ 1);
284 x_meta_map
[name_low
] = old
;
286 x_meta_map
[name_low
] = val
;
291 for (iter
= x_meta_map
.begin(); iter
!= x_meta_map
.end(); ++iter
) {
292 dout(10) << "x>> " << iter
->first
<< ":" << rgw::crypt_sanitize::x_meta_map
{iter
->first
, iter
->second
} << dendl
;
296 std::ostream
& operator<<(std::ostream
& oss
, const rgw_err
&err
)
298 oss
<< "rgw_err(http_ret=" << err
.http_ret
<< ", s3='" << err
.s3_code
<< "') ";
302 string
rgw_string_unquote(const string
& s
)
304 if (s
[0] != '"' || s
.size() < 2)
308 for (len
= s
.size(); len
> 2; --len
) {
309 if (s
[len
- 1] != ' ')
316 return s
.substr(1, len
- 2);
319 static void trim_whitespace(const string
& src
, string
& dst
)
321 const char *spacestr
= " \t\n\r\f\v";
322 int start
= src
.find_first_not_of(spacestr
);
326 int end
= src
.find_last_not_of(spacestr
);
327 dst
= src
.substr(start
, end
- start
+ 1);
330 static bool check_str_end(const char *s
)
343 static bool check_gmt_end(const char *s
)
348 while (isspace(*s
)) {
352 /* check for correct timezone */
353 if ((strncmp(s
, "GMT", 3) != 0) &&
354 (strncmp(s
, "UTC", 3) != 0)) {
361 static bool parse_rfc850(const char *s
, struct tm
*t
)
363 memset(t
, 0, sizeof(*t
));
364 return check_gmt_end(strptime(s
, "%A, %d-%b-%y %H:%M:%S ", t
));
367 static bool parse_asctime(const char *s
, struct tm
*t
)
369 memset(t
, 0, sizeof(*t
));
370 return check_str_end(strptime(s
, "%a %b %d %H:%M:%S %Y", t
));
373 static bool parse_rfc1123(const char *s
, struct tm
*t
)
375 memset(t
, 0, sizeof(*t
));
376 return check_gmt_end(strptime(s
, "%a, %d %b %Y %H:%M:%S ", t
));
379 static bool parse_rfc1123_alt(const char *s
, struct tm
*t
)
381 memset(t
, 0, sizeof(*t
));
382 return check_str_end(strptime(s
, "%a, %d %b %Y %H:%M:%S %z", t
));
385 bool parse_rfc2616(const char *s
, struct tm
*t
)
387 return parse_rfc850(s
, t
) || parse_asctime(s
, t
) || parse_rfc1123(s
, t
) || parse_rfc1123_alt(s
,t
);
390 bool parse_iso8601(const char *s
, struct tm
*t
, uint32_t *pns
, bool extended_format
)
392 memset(t
, 0, sizeof(*t
));
398 if (extended_format
) {
399 p
= strptime(s
, "%Y-%m-%dT%T", t
);
401 p
= strptime(s
, "%Y-%m-%d %T", t
);
404 p
= strptime(s
, "%Y%m%dT%H%M%S", t
);
407 dout(0) << "parse_iso8601 failed" << dendl
;
411 trim_whitespace(p
, str
);
412 int len
= str
.size();
414 if (len
== 1 && str
[0] == 'Z')
422 string nsstr
= str
.substr(1, len
- 2);
423 int r
= stringtoul(nsstr
, &ms
);
431 if (nsstr
.size() > 9) {
432 nsstr
= nsstr
.substr(0, 9);
435 uint64_t mul_table
[] = { 0,
447 *pns
= ms
* mul_table
[nsstr
.size()];
452 int parse_key_value(string
& in_str
, const char *delim
, string
& key
, string
& val
)
457 int pos
= in_str
.find(delim
);
461 trim_whitespace(in_str
.substr(0, pos
), key
);
464 trim_whitespace(in_str
.substr(pos
), val
);
469 int parse_key_value(string
& in_str
, string
& key
, string
& val
)
471 return parse_key_value(in_str
, "=", key
,val
);
474 int parse_time(const char *time_str
, real_time
*time
)
479 if (!parse_rfc2616(time_str
, &tm
) && !parse_iso8601(time_str
, &tm
, &ns
)) {
483 time_t sec
= internal_timegm(&tm
);
484 *time
= utime_t(sec
, ns
).to_real_time();
489 #define TIME_BUF_SIZE 128
491 void rgw_to_iso8601(const real_time
& t
, char *dest
, int buf_size
)
495 char buf
[TIME_BUF_SIZE
];
497 time_t epoch
= ut
.sec();
498 struct tm
*tmp
= gmtime_r(&epoch
, &result
);
502 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T", tmp
) == 0)
505 snprintf(dest
, buf_size
, "%s.%03dZ", buf
, (int)(ut
.usec() / 1000));
508 void rgw_to_iso8601(const real_time
& t
, string
*dest
)
510 char buf
[TIME_BUF_SIZE
];
511 rgw_to_iso8601(t
, buf
, sizeof(buf
));
516 * calculate the sha1 value of a given msg and key
518 void calc_hmac_sha1(const char *key
, int key_len
,
519 const char *msg
, int msg_len
, char *dest
)
520 /* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
522 HMACSHA1
hmac((const unsigned char *)key
, key_len
);
523 hmac
.Update((const unsigned char *)msg
, msg_len
);
524 hmac
.Final((unsigned char *)dest
);
528 * calculate the sha256 value of a given msg and key
530 void calc_hmac_sha256(const char *key
, int key_len
,
531 const char *msg
, int msg_len
, char *dest
)
533 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
535 HMACSHA256
hmac((const unsigned char *)key
, key_len
);
536 hmac
.Update((const unsigned char *)msg
, msg_len
);
537 hmac
.Final((unsigned char *)hash_sha256
);
539 memcpy(dest
, hash_sha256
, CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
);
543 * calculate the sha256 hash value of a given msg
545 void calc_hash_sha256(const char *msg
, int len
, string
& dest
)
547 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
550 hash
.Update((const unsigned char *)msg
, len
);
551 hash
.Final((unsigned char *)hash_sha256
);
553 char hex_str
[(CEPH_CRYPTO_SHA256_DIGESTSIZE
* 2) + 1];
554 buf_to_hex((unsigned char *)hash_sha256
, CEPH_CRYPTO_SHA256_DIGESTSIZE
, hex_str
);
556 dest
= std::string(hex_str
);
559 using ceph::crypto::SHA256
;
561 SHA256
* calc_hash_sha256_open_stream()
566 void calc_hash_sha256_update_stream(SHA256
*hash
, const char *msg
, int len
)
568 hash
->Update((const unsigned char *)msg
, len
);
571 string
calc_hash_sha256_close_stream(SHA256
**phash
)
573 SHA256
*hash
= *phash
;
575 hash
= calc_hash_sha256_open_stream();
577 char hash_sha256
[CEPH_CRYPTO_HMACSHA256_DIGESTSIZE
];
579 hash
->Final((unsigned char *)hash_sha256
);
581 char hex_str
[(CEPH_CRYPTO_SHA256_DIGESTSIZE
* 2) + 1];
582 buf_to_hex((unsigned char *)hash_sha256
, CEPH_CRYPTO_SHA256_DIGESTSIZE
, hex_str
);
587 return std::string(hex_str
);
590 int gen_rand_base64(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
593 char tmp_dest
[size
+ 4]; /* so that there's space for the extra '=' characters, and some */
596 ret
= get_random_bytes(buf
, sizeof(buf
));
598 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
602 ret
= ceph_armor(tmp_dest
, &tmp_dest
[sizeof(tmp_dest
)],
603 (const char *)buf
, ((const char *)buf
) + ((size
- 1) * 3 + 4 - 1) / 4);
605 lderr(cct
) << "ceph_armor failed" << dendl
;
608 tmp_dest
[ret
] = '\0';
609 memcpy(dest
, tmp_dest
, size
);
615 static const char alphanum_upper_table
[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
617 int gen_rand_alphanumeric_upper(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
619 int ret
= get_random_bytes(dest
, size
);
621 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
626 for (i
=0; i
<size
- 1; i
++) {
627 int pos
= (unsigned)dest
[i
];
628 dest
[i
] = alphanum_upper_table
[pos
% (sizeof(alphanum_upper_table
) - 1)];
635 static const char alphanum_lower_table
[]="0123456789abcdefghijklmnopqrstuvwxyz";
637 int gen_rand_alphanumeric_lower(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
639 int ret
= get_random_bytes(dest
, size
);
641 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
646 for (i
=0; i
<size
- 1; i
++) {
647 int pos
= (unsigned)dest
[i
];
648 dest
[i
] = alphanum_lower_table
[pos
% (sizeof(alphanum_lower_table
) - 1)];
655 int gen_rand_alphanumeric_lower(CephContext
*cct
, string
*str
, int length
)
657 char buf
[length
+ 1];
658 int ret
= gen_rand_alphanumeric_lower(cct
, buf
, sizeof(buf
));
666 // this is basically a modified base64 charset, url friendly
667 static const char alphanum_table
[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
669 int gen_rand_alphanumeric(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
671 int ret
= get_random_bytes(dest
, size
);
673 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
678 for (i
=0; i
<size
- 1; i
++) {
679 int pos
= (unsigned)dest
[i
];
680 dest
[i
] = alphanum_table
[pos
& 63];
687 static const char alphanum_no_underscore_table
[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.";
689 int gen_rand_alphanumeric_no_underscore(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
691 int ret
= get_random_bytes(dest
, size
);
693 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
698 for (i
=0; i
<size
- 1; i
++) {
699 int pos
= (unsigned)dest
[i
];
700 dest
[i
] = alphanum_no_underscore_table
[pos
& 63];
707 static const char alphanum_plain_table
[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
709 int gen_rand_alphanumeric_plain(CephContext
*cct
, char *dest
, int size
) /* size should be the required string size + 1 */
711 int ret
= get_random_bytes(dest
, size
);
713 lderr(cct
) << "cannot get random bytes: " << cpp_strerror(-ret
) << dendl
;
718 for (i
=0; i
<size
- 1; i
++) {
719 int pos
= (unsigned)dest
[i
];
720 dest
[i
] = alphanum_plain_table
[pos
% (sizeof(alphanum_plain_table
) - 1)];
729 int delim_pos
= str
.find('=');
737 name
= str
.substr(0, delim_pos
);
738 val
= str
.substr(delim_pos
+ 1);
744 int RGWHTTPArgs::parse()
756 int fpos
= str
.find('&', pos
);
761 string substr
, nameval
;
762 substr
= str
.substr(pos
, fpos
- pos
);
763 url_decode(substr
, nameval
, true);
765 int ret
= nv
.parse();
767 string
& name
= nv
.get_name();
768 string
& val
= nv
.get_val();
779 void RGWHTTPArgs::append(const string
& name
, const string
& val
)
781 if (name
.compare(0, sizeof(RGW_SYS_PARAM_PREFIX
) - 1, RGW_SYS_PARAM_PREFIX
) == 0) {
782 sys_val_map
[name
] = val
;
787 if ((name
.compare("acl") == 0) ||
788 (name
.compare("cors") == 0) ||
789 (name
.compare("location") == 0) ||
790 (name
.compare("logging") == 0) ||
791 (name
.compare("usage") == 0) ||
792 (name
.compare("lifecycle") == 0) ||
793 (name
.compare("delete") == 0) ||
794 (name
.compare("uploads") == 0) ||
795 (name
.compare("partNumber") == 0) ||
796 (name
.compare("uploadId") == 0) ||
797 (name
.compare("versionId") == 0) ||
798 (name
.compare("start-date") == 0) ||
799 (name
.compare("end-date") == 0) ||
800 (name
.compare("versions") == 0) ||
801 (name
.compare("versioning") == 0) ||
802 (name
.compare("website") == 0) ||
803 (name
.compare("requestPayment") == 0) ||
804 (name
.compare("torrent") == 0)) {
805 sub_resources
[name
] = val
;
806 } else if (name
[0] == 'r') { // root of all evil
807 if ((name
.compare("response-content-type") == 0) ||
808 (name
.compare("response-content-language") == 0) ||
809 (name
.compare("response-expires") == 0) ||
810 (name
.compare("response-cache-control") == 0) ||
811 (name
.compare("response-content-disposition") == 0) ||
812 (name
.compare("response-content-encoding") == 0)) {
813 sub_resources
[name
] = val
;
814 has_resp_modifier
= true;
816 } else if ((name
.compare("subuser") == 0) ||
817 (name
.compare("key") == 0) ||
818 (name
.compare("caps") == 0) ||
819 (name
.compare("index") == 0) ||
820 (name
.compare("policy") == 0) ||
821 (name
.compare("quota") == 0) ||
822 (name
.compare("object") == 0)) {
824 if (!admin_subresource_added
) {
825 sub_resources
[name
] = "";
826 admin_subresource_added
= true;
831 const string
& RGWHTTPArgs::get(const string
& name
, bool *exists
) const
833 auto iter
= val_map
.find(name
);
834 bool e
= (iter
!= std::end(val_map
));
842 int RGWHTTPArgs::get_bool(const string
& name
, bool *val
, bool *exists
)
844 map
<string
, string
>::iterator iter
;
845 iter
= val_map
.find(name
);
846 bool e
= (iter
!= val_map
.end());
851 const char *s
= iter
->second
.c_str();
853 if (strcasecmp(s
, "false") == 0) {
855 } else if (strcasecmp(s
, "true") == 0) {
865 int RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool *exists
)
868 return get_bool(s
, val
, exists
);
871 void RGWHTTPArgs::get_bool(const char *name
, bool *val
, bool def_val
)
874 if ((get_bool(name
, val
, &exists
) < 0) ||
880 string
RGWHTTPArgs::sys_get(const string
& name
, bool * const exists
) const
882 const auto iter
= sys_val_map
.find(name
);
883 const bool e
= (iter
!= sys_val_map
.end());
889 return e
? iter
->second
: string();
892 bool verify_user_permission(struct req_state
* const s
,
893 RGWAccessControlPolicy
* const user_acl
,
896 /* S3 doesn't support account ACLs. */
900 if ((perm
& (int)s
->perm_mask
) != perm
)
903 return user_acl
->verify_permission(*s
->auth
.identity
, perm
, perm
);
906 bool verify_user_permission(struct req_state
* const s
,
909 return verify_user_permission(s
, s
->user_acl
.get(), perm
);
912 bool verify_requester_payer_permission(struct req_state
*s
)
914 if (!s
->bucket_info
.requester_pays
)
917 if (s
->auth
.identity
->is_owner_of(s
->bucket_info
.owner
))
920 if (s
->auth
.identity
->is_anonymous()) {
924 const char *request_payer
= s
->info
.env
->get("HTTP_X_AMZ_REQUEST_PAYER");
925 if (!request_payer
) {
927 request_payer
= s
->info
.args
.get("x-amz-request-payer", &exists
).c_str();
933 if (strcasecmp(request_payer
, "requester") == 0) {
940 bool verify_bucket_permission(struct req_state
* const s
,
941 RGWAccessControlPolicy
* const user_acl
,
942 RGWAccessControlPolicy
* const bucket_acl
,
948 if ((perm
& (int)s
->perm_mask
) != perm
)
951 if (!verify_requester_payer_permission(s
))
954 if (bucket_acl
->verify_permission(*s
->auth
.identity
, perm
, perm
,
955 s
->info
.env
->get("HTTP_REFERER")))
961 return user_acl
->verify_permission(*s
->auth
.identity
, perm
, perm
);
964 bool verify_bucket_permission(struct req_state
* const s
, const int perm
)
966 return verify_bucket_permission(s
,
972 static inline bool check_deferred_bucket_acl(struct req_state
* const s
,
973 RGWAccessControlPolicy
* const user_acl
,
974 RGWAccessControlPolicy
* const bucket_acl
,
975 const uint8_t deferred_check
,
978 return (s
->defer_to_bucket_acls
== deferred_check \
979 && verify_bucket_permission(s
, user_acl
, bucket_acl
, perm
));
982 bool verify_object_permission(struct req_state
* const s
,
983 RGWAccessControlPolicy
* const user_acl
,
984 RGWAccessControlPolicy
* const bucket_acl
,
985 RGWAccessControlPolicy
* const object_acl
,
988 if (!verify_requester_payer_permission(s
))
991 if (check_deferred_bucket_acl(s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_RECURSE
, perm
) ||
992 check_deferred_bucket_acl(s
, user_acl
, bucket_acl
, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL
, RGW_PERM_FULL_CONTROL
)) {
1000 bool ret
= object_acl
->verify_permission(*s
->auth
.identity
, s
->perm_mask
, perm
);
1005 if (!s
->cct
->_conf
->rgw_enforce_swift_acls
)
1008 if ((perm
& (int)s
->perm_mask
) != perm
)
1012 if (perm
& (RGW_PERM_READ
| RGW_PERM_READ_ACP
))
1013 swift_perm
|= RGW_PERM_READ_OBJS
;
1014 if (perm
& RGW_PERM_WRITE
)
1015 swift_perm
|= RGW_PERM_WRITE_OBJS
;
1020 /* we already verified the user mask above, so we pass swift_perm as the mask here,
1021 otherwise the mask might not cover the swift permissions bits */
1022 if (bucket_acl
->verify_permission(*s
->auth
.identity
, swift_perm
, swift_perm
,
1023 s
->info
.env
->get("HTTP_REFERER")))
1029 return user_acl
->verify_permission(*s
->auth
.identity
, swift_perm
, swift_perm
);
1032 bool verify_object_permission(struct req_state
*s
, int perm
)
1034 return verify_object_permission(s
,
1047 memset(table
, -1, sizeof(table
));
1049 for (i
= '0'; i
<='9'; i
++)
1051 for (i
= 'A'; i
<='F'; i
++)
1052 table
[i
] = i
- 'A' + 0xa;
1053 for (i
= 'a'; i
<='f'; i
++)
1054 table
[i
] = i
- 'a' + 0xa;
1057 char to_num(char c
) {
1058 return table
[(int)c
];
1062 static char hex_to_num(char c
)
1064 static HexTable hex_table
;
1065 return hex_table
.to_num(c
);
1068 bool url_decode(const string
& src_str
, string
& dest_str
, bool in_query
)
1070 const char *src
= src_str
.c_str();
1071 char dest
[src_str
.size() + 1];
1077 if (!in_query
|| *src
!= '+') {
1078 if (*src
== '?') in_query
= true;
1079 dest
[pos
++] = *src
++;
1088 char c1
= hex_to_num(*src
++);
1094 c1
= hex_to_num(*src
++);
1107 void rgw_uri_escape_char(char c
, string
& dst
)
1110 snprintf(buf
, sizeof(buf
), "%%%.2X", (int)(unsigned char)c
);
1114 static bool char_needs_url_encoding(char c
)
1116 if (c
<= 0x20 || c
>= 0x7f)
1146 void url_encode(const string
& src
, string
& dst
)
1148 const char *p
= src
.c_str();
1149 for (unsigned i
= 0; i
< src
.size(); i
++, p
++) {
1150 if (char_needs_url_encoding(*p
)) {
1151 rgw_uri_escape_char(*p
, dst
);
1159 std::string
url_encode(const std::string
& src
)
1162 url_encode(src
, dst
);
1167 string
rgw_trim_whitespace(const string
& src
)
1174 for (; start
!= (int)src
.size(); start
++) {
1175 if (!isspace(src
[start
]))
1179 int end
= src
.size() - 1;
1184 for (; end
> start
; end
--) {
1185 if (!isspace(src
[end
]))
1189 return src
.substr(start
, end
- start
+ 1);
1192 boost::string_ref
rgw_trim_whitespace(const boost::string_ref
& src
)
1194 boost::string_ref res
= src
;
1196 while (res
.size() > 0 && std::isspace(res
.front())) {
1197 res
.remove_prefix(1);
1199 while (res
.size() > 0 && std::isspace(res
.back())) {
1200 res
.remove_suffix(1);
1205 string
rgw_trim_quotes(const string
& val
)
1207 string s
= rgw_trim_whitespace(val
);
1212 int end
= s
.size() - 1;
1213 int quotes_count
= 0;
1215 if (s
[start
] == '"') {
1219 if (s
[end
] == '"') {
1223 if (quotes_count
== 2) {
1224 return s
.substr(start
, end
- start
+ 1);
1229 struct rgw_name_to_flag
{
1230 const char *type_name
;
1234 static int parse_list_of_flags(struct rgw_name_to_flag
*mapping
,
1235 const string
& str
, uint32_t *perm
)
1238 get_str_list(str
, strs
);
1239 list
<string
>::iterator iter
;
1241 for (iter
= strs
.begin(); iter
!= strs
.end(); ++iter
) {
1243 for (int i
= 0; mapping
[i
].type_name
; i
++) {
1244 if (s
.compare(mapping
[i
].type_name
) == 0)
1245 v
|= mapping
[i
].flag
;
1253 static struct rgw_name_to_flag cap_names
[] = { {"*", RGW_CAP_ALL
},
1254 {"read", RGW_CAP_READ
},
1255 {"write", RGW_CAP_WRITE
},
1258 int RGWUserCaps::parse_cap_perm(const string
& str
, uint32_t *perm
)
1260 return parse_list_of_flags(cap_names
, str
, perm
);
1263 int RGWUserCaps::get_cap(const string
& cap
, string
& type
, uint32_t *pperm
)
1265 int pos
= cap
.find('=');
1267 trim_whitespace(cap
.substr(0, pos
), type
);
1270 if (!is_valid_cap_type(type
))
1271 return -ERR_INVALID_CAP
;
1275 if (pos
< (int)cap
.size() - 1) {
1276 cap_perm
= cap
.substr(pos
+ 1);
1277 int r
= RGWUserCaps::parse_cap_perm(cap_perm
, &perm
);
1287 int RGWUserCaps::add_cap(const string
& cap
)
1292 int r
= get_cap(cap
, type
, &perm
);
1301 int RGWUserCaps::remove_cap(const string
& cap
)
1306 int r
= get_cap(cap
, type
, &perm
);
1310 map
<string
, uint32_t>::iterator iter
= caps
.find(type
);
1311 if (iter
== caps
.end())
1314 uint32_t& old_perm
= iter
->second
;
1322 int RGWUserCaps::add_from_string(const string
& str
)
1326 int end
= str
.find(';', start
);
1330 int r
= add_cap(str
.substr(start
, end
- start
));
1335 } while (start
< (int)str
.size());
1340 int RGWUserCaps::remove_from_string(const string
& str
)
1344 int end
= str
.find(';', start
);
1348 int r
= remove_cap(str
.substr(start
, end
- start
));
1353 } while (start
< (int)str
.size());
1358 void RGWUserCaps::dump(Formatter
*f
) const
1363 void RGWUserCaps::dump(Formatter
*f
, const char *name
) const
1365 f
->open_array_section(name
);
1366 map
<string
, uint32_t>::const_iterator iter
;
1367 for (iter
= caps
.begin(); iter
!= caps
.end(); ++iter
)
1369 f
->open_object_section("cap");
1370 f
->dump_string("type", iter
->first
);
1371 uint32_t perm
= iter
->second
;
1373 for (int i
=0; cap_names
[i
].type_name
; i
++) {
1374 if ((perm
& cap_names
[i
].flag
) == cap_names
[i
].flag
) {
1375 if (perm_str
.size())
1376 perm_str
.append(", ");
1378 perm_str
.append(cap_names
[i
].type_name
);
1379 perm
&= ~cap_names
[i
].flag
;
1382 if (perm_str
.empty())
1383 perm_str
= "<none>";
1385 f
->dump_string("perm", perm_str
);
1396 void decode_json(JSONObj
*obj
) {
1397 JSONDecoder::decode_json("type", type
, obj
);
1399 JSONDecoder::decode_json("perm", perm_str
, obj
);
1400 if (RGWUserCaps::parse_cap_perm(perm_str
, &perm
) < 0) {
1401 throw JSONDecoder::err("failed to parse permissions");
1406 void RGWUserCaps::decode_json(JSONObj
*obj
)
1408 list
<RGWUserCap
> caps_list
;
1409 decode_json_obj(caps_list
, obj
);
1411 list
<RGWUserCap
>::iterator iter
;
1412 for (iter
= caps_list
.begin(); iter
!= caps_list
.end(); ++iter
) {
1413 RGWUserCap
& cap
= *iter
;
1414 caps
[cap
.type
] = cap
.perm
;
1418 int RGWUserCaps::check_cap(const string
& cap
, uint32_t perm
)
1420 map
<string
, uint32_t>::iterator iter
= caps
.find(cap
);
1422 if ((iter
== caps
.end()) ||
1423 (iter
->second
& perm
) != perm
) {
1430 bool RGWUserCaps::is_valid_cap_type(const string
& tp
)
1432 static const char *cap_type
[] = { "user",
1443 for (unsigned int i
= 0; i
< sizeof(cap_type
) / sizeof(char *); ++i
) {
1444 if (tp
.compare(cap_type
[i
]) == 0) {
1452 static ssize_t
unescape_str(const string
& s
, ssize_t ofs
, char esc_char
, char special_char
, string
*dest
)
1454 const char *src
= s
.c_str();
1455 char dest_buf
[s
.size() + 1];
1456 char *destp
= dest_buf
;
1461 for (size_t i
= ofs
; i
< s
.size(); i
++) {
1463 if (!esc
&& c
== esc_char
) {
1467 if (!esc
&& c
== special_char
) {
1470 return (ssize_t
)i
+ 1;
1477 return string::npos
;
1480 static void escape_str(const string
& s
, char esc_char
, char special_char
, string
*dest
)
1482 const char *src
= s
.c_str();
1483 char dest_buf
[s
.size() * 2 + 1];
1484 char *destp
= dest_buf
;
1486 for (size_t i
= 0; i
< s
.size(); i
++) {
1488 if (c
== esc_char
|| c
== special_char
) {
1489 *destp
++ = esc_char
;
1497 void rgw_pool::from_str(const string
& s
)
1499 size_t pos
= unescape_str(s
, 0, '\\', ':', &name
);
1500 if (pos
!= string::npos
) {
1501 pos
= unescape_str(s
, pos
, '\\', ':', &ns
);
1502 /* ignore return; if pos != string::npos it means that we had a colon
1503 * in the middle of ns that wasn't escaped, we're going to stop there
1508 string
rgw_pool::to_str() const
1511 escape_str(name
, '\\', ':', &esc_name
);
1516 escape_str(ns
, '\\', ':', &esc_ns
);
1517 return esc_name
+ ":" + esc_ns
;
1520 void rgw_raw_obj::decode_from_rgw_obj(bufferlist::iterator
& bl
)
1523 ::decode(old_obj
, bl
);
1525 get_obj_bucket_and_oid_loc(old_obj
, oid
, loc
);
1526 pool
= old_obj
.get_explicit_data_pool();
1529 std::string
rgw_bucket::get_key(char tenant_delim
, char id_delim
) const
1531 static constexpr size_t shard_len
{12}; // ":4294967295\0"
1532 const size_t max_len
= tenant
.size() + sizeof(tenant_delim
) +
1533 name
.size() + sizeof(id_delim
) + bucket_id
.size() + shard_len
;
1536 key
.reserve(max_len
);
1537 if (!tenant
.empty() && tenant_delim
) {
1539 key
.append(1, tenant_delim
);
1542 if (!bucket_id
.empty() && id_delim
) {
1543 key
.append(1, id_delim
);
1544 key
.append(bucket_id
);
1549 std::string
rgw_bucket_shard::get_key(char tenant_delim
, char id_delim
,
1550 char shard_delim
) const
1552 auto key
= bucket
.get_key(tenant_delim
, id_delim
);
1553 if (shard_id
>= 0 && shard_delim
) {
1554 key
.append(1, shard_delim
);
1555 key
.append(std::to_string(shard_id
));
1560 static struct rgw_name_to_flag op_type_mapping
[] = { {"*", RGW_OP_TYPE_ALL
},
1561 {"read", RGW_OP_TYPE_READ
},
1562 {"write", RGW_OP_TYPE_WRITE
},
1563 {"delete", RGW_OP_TYPE_DELETE
},
1567 int rgw_parse_op_type_list(const string
& str
, uint32_t *perm
)
1569 return parse_list_of_flags(op_type_mapping
, str
, perm
);
1572 static int match_internal(boost::string_ref pattern
, boost::string_ref input
, int (*function
)(const char&, const char&))
1574 boost::string_ref::iterator it1
= pattern
.begin();
1575 boost::string_ref::iterator it2
= input
.begin();
1577 if (it1
== pattern
.end() && it2
== input
.end())
1579 if (it1
== pattern
.end() || it2
== input
.end())
1581 if (*it1
== '*' && (it1
+ 1) == pattern
.end() && it2
!= input
.end())
1583 if (*it1
== '*' && (it1
+ 1) == pattern
.end() && it2
== input
.end())
1585 if (function(*it1
, *it2
) || *it1
== '?') {
1591 if (function(*(it1
+ 1), *it2
))
1602 static int matchcase(const char& c1
, const char& c2
)
1609 static int matchignorecase(const char& c1
, const char& c2
)
1611 if (tolower(c1
) == tolower(c2
))
1616 int match(const string
& pattern
, const string
& input
, int flag
)
1618 auto last_pos_input
= 0, last_pos_pattern
= 0;
1621 auto cur_pos_input
= input
.find(":", last_pos_input
);
1622 auto cur_pos_pattern
= pattern
.find(":", last_pos_pattern
);
1624 string substr_input
= input
.substr(last_pos_input
, cur_pos_input
);
1625 string substr_pattern
= pattern
.substr(last_pos_pattern
, cur_pos_pattern
);
1628 if (flag
& POLICY_ACTION
|| flag
& POLICY_ARN
) {
1629 res
= match_internal(substr_pattern
, substr_input
, &matchignorecase
);
1631 res
= match_internal(substr_pattern
, substr_input
, &matchcase
);
1636 if (cur_pos_pattern
== string::npos
&& cur_pos_input
== string::npos
)
1638 else if ((cur_pos_pattern
== string::npos
&& cur_pos_input
!= string::npos
) ||
1639 (cur_pos_pattern
!= string::npos
&& cur_pos_input
== string::npos
))
1642 last_pos_pattern
= cur_pos_pattern
+ 1;
1643 last_pos_input
= cur_pos_input
+ 1;