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