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