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