#include <utility>
#include <boost/regex.hpp>
-
+#include <iostream>
#include "rapidjson/reader.h"
+#include "common/backport14.h"
#include "rgw_auth.h"
+#include <arpa/inet.h>
#include "rgw_iam_policy.h"
namespace {
if ((s == "*") && wildcards) {
return ARN(Partition::wildcard, Service::wildcard, "*", "*", "*");
- } else if (regex_match(s, match, wildcards ? rx_wild : rx_no_wild)) {
- ceph_assert(match.size() == 6);
-
- ARN a;
- {
- auto p = to_partition(match[1], wildcards);
- if (!p)
- return none;
-
- a.partition = *p;
- }
- {
- auto s = to_service(match[2], wildcards);
- if (!s) {
- return none;
+ } else if (regex_match(s, match, wildcards ? rx_wild : rx_no_wild) &&
+ match.size() == 6) {
+ if (auto p = to_partition(match[1], wildcards)) {
+ if (auto s = to_service(match[2], wildcards)) {
+ return ARN(*p, *s, match[3], match[4], match[5]);
}
- a.service = *s;
}
-
- a.region = match[3];
- a.account = match[4];
- a.resource = match[5];
-
- return a;
}
return none;
}
return false;
}
- if (!::match(region, candidate.region, MATCH_POLICY_ARN)) {
+ if (!match_policy(region, candidate.region, MATCH_POLICY_ARN)) {
return false;
}
- if (!::match(account, candidate.account, MATCH_POLICY_ARN)) {
+ if (!match_policy(account, candidate.account, MATCH_POLICY_ARN)) {
return false;
}
- if (!::match(resource, candidate.resource, MATCH_POLICY_ARN)) {
+ if (!match_policy(resource, candidate.resource, MATCH_POLICY_ARN)) {
return false;
}
{ "s3:DeleteBucketWebsite", s3DeleteBucketWebsite },
{ "s3:DeleteObject", s3DeleteObject },
{ "s3:DeleteObjectVersion", s3DeleteObjectVersion },
+ { "s3:DeleteObjectTagging", s3DeleteObjectTagging },
+ { "s3:DeleteObjectVersionTagging", s3DeleteObjectVersionTagging },
{ "s3:DeleteReplicationConfiguration", s3DeleteReplicationConfiguration },
{ "s3:GetAccelerateConfiguration", s3GetAccelerateConfiguration },
{ "s3:GetBucketAcl", s3GetBucketAcl },
{ "s3:GetObjectVersionAcl", s3GetObjectVersionAcl },
{ "s3:GetObjectVersion", s3GetObjectVersion },
{ "s3:GetObjectVersionTorrent", s3GetObjectVersionTorrent },
+ { "s3:GetObjectTagging", s3GetObjectTagging },
+ { "s3:GetObjectVersionTagging", s3GetObjectVersionTagging},
{ "s3:GetReplicationConfiguration", s3GetReplicationConfiguration },
{ "s3:ListAllMyBuckets", s3ListAllMyBuckets },
{ "s3:ListBucketMultiPartUploads", s3ListBucketMultiPartUploads },
{ "s3:PutObjectAcl", s3PutObjectAcl },
{ "s3:PutObject", s3PutObject },
{ "s3:PutObjectVersionAcl", s3PutObjectVersionAcl },
+ { "s3:PutObjectTagging", s3PutObjectTagging },
+ { "s3:PutObjectVersionTagging", s3PutObjectVersionTagging },
{ "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
{ "s3:RestoreObject", s3RestoreObject }};
bool arraying = false;
bool objecting = false;
+ bool cond_ifexists = false;
void reset();
CephContext* cct;
const string& tenant;
Policy& policy;
+ uint32_t v = 0;
uint32_t seen = 0;
}
void set(TokenID in) {
seen |= dex(in);
+ if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
+ dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
+ dex(TokenID::Action) | dex(TokenID::NotAction) |
+ dex(TokenID::Resource) | dex(TokenID::NotResource) |
+ dex(TokenID::Condition) | dex(TokenID::AWS) |
+ dex(TokenID::Federated) | dex(TokenID::Service) |
+ dex(TokenID::CanonicalUser))) {
+ v |= dex(in);
+ }
}
void set(std::initializer_list<TokenID> l) {
for (auto in : l) {
seen |= dex(in);
+ if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
+ dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
+ dex(TokenID::Action) | dex(TokenID::NotAction) |
+ dex(TokenID::Resource) | dex(TokenID::NotResource) |
+ dex(TokenID::Condition) | dex(TokenID::AWS) |
+ dex(TokenID::Federated) | dex(TokenID::Service) |
+ dex(TokenID::CanonicalUser))) {
+ v |= dex(in);
+ }
}
}
void reset(TokenID in) {
seen &= ~dex(in);
+ if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
+ dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
+ dex(TokenID::Action) | dex(TokenID::NotAction) |
+ dex(TokenID::Resource) | dex(TokenID::NotResource) |
+ dex(TokenID::Condition) | dex(TokenID::AWS) |
+ dex(TokenID::Federated) | dex(TokenID::Service) |
+ dex(TokenID::CanonicalUser))) {
+ v &= ~dex(in);
+ }
}
void reset(std::initializer_list<TokenID> l) {
for (auto in : l) {
seen &= ~dex(in);
+ if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
+ dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
+ dex(TokenID::Action) | dex(TokenID::NotAction) |
+ dex(TokenID::Resource) | dex(TokenID::NotResource) |
+ dex(TokenID::Condition) | dex(TokenID::AWS) |
+ dex(TokenID::Federated) | dex(TokenID::Service) |
+ dex(TokenID::CanonicalUser))) {
+ v &= ~dex(in);
+ }
}
}
+ void reset(uint32_t& v) {
+ seen &= ~v;
+ v = 0;
+ }
PolicyParser(CephContext* cct, const string& tenant, Policy& policy)
: cct(cct), tenant(tenant), policy(policy) {}
if (s.empty()) {
return false;
}
-
return s.back().obj_end();
}
bool Key(const char* str, SizeType length, bool copy) {
if (s.empty()) {
return false;
}
-
return s.back().key(str, length);
}
if (s.empty()) {
return false;
}
-
return s.back().do_string(cct, str, length);
}
bool RawNumber(const char* str, SizeType length, bool copy) {
}
bool ParseState::key(const char* s, size_t l) {
- auto k = pp->tokens.lookup(s, l);
+ auto token_len = l;
+ bool ifexists = false;
+ if (w->id == TokenID::Condition && w->kind == TokenKind::statement) {
+ static constexpr char IfExists[] = "IfExists";
+ if (boost::algorithm::ends_with(boost::string_view{s, l}, IfExists)) {
+ ifexists = true;
+ token_len -= sizeof(IfExists)-1;
+ }
+ }
+ auto k = pp->tokens.lookup(s, token_len);
if (!k) {
if (w->kind == TokenKind::cond_op) {
+ auto id = w->id;
auto& t = pp->policy.statements.back();
+ auto c_ife = cond_ifexists;
pp->s.emplace_back(pp, cond_key);
- t.conditions.emplace_back(w->id, s, l);
+ t.conditions.emplace_back(id, s, l, c_ife);
return true;
} else {
return false;
// If the token we're going with belongs within the condition at the
// top of the stack and we haven't already encountered it, push it
// on the stack
-
// Top
if ((((w->id == TokenID::Top) && (k->kind == TokenKind::top)) ||
// Statement
} else if ((w->id == TokenID::Condition) &&
(k->kind == TokenKind::cond_op)) {
pp->s.emplace_back(pp, k);
+ pp->s.back().cond_ifexists = ifexists;
return true;
}
return false;
// I should just rewrite a few helper functions to use iterators,
// which will make all of this ever so much nicer.
static optional<Principal> parse_principal(CephContext* cct, TokenID t,
- string&& s) {
+ string&& s) {
// Wildcard!
if ((t == TokenID::AWS) && (s == "*")) {
return Principal::wildcard();
// AWS ARNs
} else if (t == TokenID::AWS) {
- auto a = ARN::parse(s);
- if (!a) {
+ if (auto a = ARN::parse(s)) {
+ if (a->resource == "root") {
+ return Principal::tenant(std::move(a->account));
+ }
+
+ static const char rx_str[] = "([^/]*)/(.*)";
+ static const regex rx(rx_str, sizeof(rx_str) - 1,
+ ECMAScript | optimize);
+ smatch match;
+ if (regex_match(a->resource, match, rx) && match.size() == 3) {
+ if (match[1] == "user") {
+ return Principal::user(std::move(a->account),
+ match[2]);
+ }
+
+ if (match[1] == "role") {
+ return Principal::role(std::move(a->account),
+ match[2]);
+ }
+ }
+ } else {
if (std::none_of(s.begin(), s.end(),
[](const char& c) {
return (c == ':') || (c == '/');
return Principal::tenant(std::move(s));
}
}
-
- if (a->resource == "root") {
- return Principal::tenant(std::move(a->account));
- }
-
- static const char rx_str[] = "([^/]*)/(.*)";
- static const regex rx(rx_str, sizeof(rx_str) - 1,
- ECMAScript | optimize);
- smatch match;
- if (regex_match(a->resource, match, rx)) {
- ceph_assert(match.size() == 2);
-
- if (match[1] == "user") {
- return Principal::user(std::move(a->account),
- match[2]);
- }
-
- if (match[1] == "role") {
- return Principal::role(std::move(a->account),
- match[2]);
- }
- }
}
ldout(cct, 0) << "Supplied principal is discarded: " << s << dendl;
} else if (w->id == TokenID::Sid) {
t->sid.emplace(s, l);
- } else if ((w->id == TokenID::Effect) &&
+ } else if ((w->id == TokenID::Effect) && k &&
k->kind == TokenKind::effect_key) {
t->effect = static_cast<Effect>(k->specific);
} else if (w->id == TokenID::Principal && s && *s == '*') {
} else if ((w->id == TokenID::Action) ||
(w->id == TokenID::NotAction)) {
for (auto& p : actpairs) {
- if (match({s, l}, p.name, MATCH_POLICY_ACTION)) {
+ if (match_policy({s, l}, p.name, MATCH_POLICY_ACTION)) {
(w->id == TokenID::Action ? t->action : t->notaction) |= p.bit;
}
}
// Principals
} else if (w->kind == TokenKind::princ_type) {
- ceph_assert(pp->s.size() > 1);
+ if (pp->s.size() <= 1) {
+ return false;
+ }
auto& pri = pp->s[pp->s.size() - 2].w->id == TokenID::Principal ?
t->princ : t->noprinc;
- auto o = parse_principal(pp->cct, w->id, string(s, l));
- if (o)
+
+ if (auto o = parse_principal(pp->cct, w->id, string(s, l))) {
pri.emplace(std::move(*o));
+ }
// Failure
}
void ParseState::reset() {
- pp->reset({TokenID::Sid, TokenID::Effect, TokenID::Principal,
- TokenID::NotPrincipal, TokenID::Action, TokenID::NotAction,
- TokenID::Resource, TokenID::NotResource, TokenID::Condition});
+ pp->reset(pp->v);
}
bool ParseState::obj_start() {
ostream& operator <<(ostream& m, const MaskedIP& ip) {
// I have a theory about why std::bitset is the way it is.
if (ip.v6) {
- for (int i = 15; i >= 0; --i) {
- uint8_t b = 0;
- for (int j = 7; j >= 0; --j) {
- b |= (ip.addr[(i * 8) + j] << j);
+ for (int i = 7; i >= 0; --i) {
+ uint16_t hextet = 0;
+ for (int j = 15; j >= 0; --j) {
+ hextet |= (ip.addr[(i * 16) + j] << j);
}
- m << hex << b;
+ m << hex << (unsigned int) hextet;
if (i != 0) {
- m << "::";
+ m << ":";
}
}
} else {
for (int j = 7; j >= 0; --j) {
b |= (ip.addr[(i * 8) + j] << j);
}
- m << b;
+ m << (unsigned int) b;
if (i != 0) {
m << ".";
}
}
}
- m << "/" << ip.prefix;
+ m << "/" << dec << ip.prefix;
// It would explain a lot
return m;
}
}
if (i == env.end()) {
- return false;
+ return ifexists;
}
const auto& s = i->second;
return orrible(std::equal_to<std::string>(), s, vals);
case TokenID::StringNotEquals:
- return orrible(std::not2(std::equal_to<std::string>()),
+ return orrible(ceph::not_fn(std::equal_to<std::string>()),
s, vals);
case TokenID::StringEqualsIgnoreCase:
return orrible(ci_equal_to(), s, vals);
case TokenID::StringNotEqualsIgnoreCase:
- return orrible(std::not2(ci_equal_to()), s, vals);
+ return orrible(ceph::not_fn(ci_equal_to()), s, vals);
- // Implement actual StringLike with wildcarding later
case TokenID::StringLike:
- return orrible(std::equal_to<std::string>(), s, vals);
+ return orrible(string_like(), s, vals);
+
case TokenID::StringNotLike:
- return orrible(std::not2(std::equal_to<std::string>()),
- s, vals);
+ return orrible(ceph::not_fn(string_like()), s, vals);
// Numeric
case TokenID::NumericEquals:
return shortible(std::equal_to<double>(), as_number, s, vals);
case TokenID::NumericNotEquals:
- return shortible(std::not2(std::equal_to<double>()),
+ return shortible(ceph::not_fn(std::equal_to<double>()),
as_number, s, vals);
return shortible(std::equal_to<ceph::real_time>(), as_date, s, vals);
case TokenID::DateNotEquals:
- return shortible(std::not2(std::equal_to<ceph::real_time>()),
+ return shortible(ceph::not_fn(std::equal_to<ceph::real_time>()),
as_date, s, vals);
case TokenID::DateLessThan:
return shortible(std::equal_to<MaskedIP>(), as_network, s, vals);
case TokenID::NotIpAddress:
- return shortible(std::not2(std::equal_to<MaskedIP>()), as_network, s,
- vals);
+ {
+ auto xc = as_network(s);
+ if (!xc) {
+ return false;
+ }
+
+ for (const string& d : vals) {
+ auto xd = as_network(d);
+ if (!xd) {
+ continue;
+ }
+
+ if (xc == xd) {
+ return false;
+ }
+ }
+ return true;
+ }
#if 0
// Amazon Resource Names! (Does S3 need this?)
return none;
}
- m.v6 = s.find(':');
+ m.v6 = (s.find(':') == string::npos) ? false : true;
auto slash = s.find('/');
if (slash == string::npos) {
m.prefix = m.v6 ? 128 : 32;
}
if (m.v6) {
- struct sockaddr_in6 a;
+ struct in6_addr a;
if (inet_pton(AF_INET6, p->c_str(), static_cast<void*>(&a)) != 1) {
return none;
}
- m.addr |= Address(a.sin6_addr.s6_addr[0]) << 0;
- m.addr |= Address(a.sin6_addr.s6_addr[1]) << 8;
- m.addr |= Address(a.sin6_addr.s6_addr[2]) << 16;
- m.addr |= Address(a.sin6_addr.s6_addr[3]) << 24;
- m.addr |= Address(a.sin6_addr.s6_addr[4]) << 32;
- m.addr |= Address(a.sin6_addr.s6_addr[5]) << 40;
- m.addr |= Address(a.sin6_addr.s6_addr[6]) << 48;
- m.addr |= Address(a.sin6_addr.s6_addr[7]) << 56;
- m.addr |= Address(a.sin6_addr.s6_addr[8]) << 64;
- m.addr |= Address(a.sin6_addr.s6_addr[9]) << 72;
- m.addr |= Address(a.sin6_addr.s6_addr[10]) << 80;
- m.addr |= Address(a.sin6_addr.s6_addr[11]) << 88;
- m.addr |= Address(a.sin6_addr.s6_addr[12]) << 96;
- m.addr |= Address(a.sin6_addr.s6_addr[13]) << 104;
- m.addr |= Address(a.sin6_addr.s6_addr[14]) << 112;
- m.addr |= Address(a.sin6_addr.s6_addr[15]) << 120;
+ m.addr |= Address(a.s6_addr[15]) << 0;
+ m.addr |= Address(a.s6_addr[14]) << 8;
+ m.addr |= Address(a.s6_addr[13]) << 16;
+ m.addr |= Address(a.s6_addr[12]) << 24;
+ m.addr |= Address(a.s6_addr[11]) << 32;
+ m.addr |= Address(a.s6_addr[10]) << 40;
+ m.addr |= Address(a.s6_addr[9]) << 48;
+ m.addr |= Address(a.s6_addr[8]) << 56;
+ m.addr |= Address(a.s6_addr[7]) << 64;
+ m.addr |= Address(a.s6_addr[6]) << 72;
+ m.addr |= Address(a.s6_addr[5]) << 80;
+ m.addr |= Address(a.s6_addr[4]) << 88;
+ m.addr |= Address(a.s6_addr[3]) << 96;
+ m.addr |= Address(a.s6_addr[2]) << 104;
+ m.addr |= Address(a.s6_addr[1]) << 112;
+ m.addr |= Address(a.s6_addr[0]) << 120;
} else {
- struct sockaddr_in a;
+ struct in_addr a;
if (inet_pton(AF_INET, p->c_str(), static_cast<void*>(&a)) != 1) {
return none;
}
- m.addr = ntohl(a.sin_addr.s_addr);
+
+ m.addr = ntohl(a.s_addr);
}
- return none;
+ return m;
}
namespace {
case s3DeleteReplicationConfiguration:
return "s3:DeleteReplicationConfiguration";
+
+ case s3PutObjectTagging:
+ return "s3:PutObjectTagging";
+
+ case s3PutObjectVersionTagging:
+ return "s3:PutObjectVersionTagging";
+
+ case s3GetObjectTagging:
+ return "s3:GetObjectTagging";
+
+ case s3GetObjectVersionTagging:
+ return "s3:GetObjectVersionTagging";
+
+ case s3DeleteObjectTagging:
+ return "s3:DeleteObjectTagging";
+
+ case s3DeleteObjectVersionTagging:
+ return "s3:DeleteObjectVersionTagging";
}
return "s3Invalid";
}