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