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