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