]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/rgw/rgw_iam_policy.cc
update sources to v12.2.3
[ceph.git] / ceph / src / rgw / rgw_iam_policy.cc
index 843dca7a2de09cdb1dbd979e98351627234d5619..675db22c21603b80d9032af5f9c8b979d94dc46d 100644 (file)
@@ -8,10 +8,12 @@
 #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 {
@@ -218,30 +220,13 @@ optional<ARN> ARN::parse(const string& s, bool wildcards) {
 
   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;
 }
@@ -388,15 +373,15 @@ bool ARN::match(const ARN& candidate) const {
     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;
   }
 
@@ -411,6 +396,8 @@ static const actpair actpairs[] =
  { "s3:DeleteBucketWebsite", s3DeleteBucketWebsite },
  { "s3:DeleteObject", s3DeleteObject },
  { "s3:DeleteObjectVersion", s3DeleteObjectVersion },
+ { "s3:DeleteObjectTagging", s3DeleteObjectTagging },
+ { "s3:DeleteObjectVersionTagging", s3DeleteObjectVersionTagging },
  { "s3:DeleteReplicationConfiguration", s3DeleteReplicationConfiguration },
  { "s3:GetAccelerateConfiguration", s3GetAccelerateConfiguration },
  { "s3:GetBucketAcl", s3GetBucketAcl },
@@ -430,6 +417,8 @@ static const actpair actpairs[] =
  { "s3:GetObjectVersionAcl", s3GetObjectVersionAcl },
  { "s3:GetObjectVersion", s3GetObjectVersion },
  { "s3:GetObjectVersionTorrent", s3GetObjectVersionTorrent },
+ { "s3:GetObjectTagging", s3GetObjectTagging },
+ { "s3:GetObjectVersionTagging", s3GetObjectVersionTagging},
  { "s3:GetReplicationConfiguration", s3GetReplicationConfiguration },
  { "s3:ListAllMyBuckets", s3ListAllMyBuckets },
  { "s3:ListBucketMultiPartUploads", s3ListBucketMultiPartUploads },
@@ -450,6 +439,8 @@ static const actpair actpairs[] =
  { "s3:PutObjectAcl",  s3PutObjectAcl },
  { "s3:PutObject", s3PutObject },
  { "s3:PutObjectVersionAcl", s3PutObjectVersionAcl },
+ { "s3:PutObjectTagging", s3PutObjectTagging },
+ { "s3:PutObjectVersionTagging", s3PutObjectVersionTagging },
  { "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
  { "s3:RestoreObject", s3RestoreObject }};
 
@@ -466,6 +457,7 @@ struct ParseState {
 
   bool arraying = false;
   bool objecting = false;
+  bool cond_ifexists = false;
 
   void reset();
 
@@ -498,6 +490,7 @@ struct PolicyParser : public BaseReaderHandler<UTF8<>, PolicyParser> {
   CephContext* cct;
   const string& tenant;
   Policy& policy;
+  uint32_t v = 0;
 
   uint32_t seen = 0;
 
@@ -544,20 +537,60 @@ struct PolicyParser : public BaseReaderHandler<UTF8<>, PolicyParser> {
   }
   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) {}
@@ -576,14 +609,12 @@ struct PolicyParser : public BaseReaderHandler<UTF8<>, PolicyParser> {
     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);
   }
 
@@ -591,7 +622,6 @@ struct PolicyParser : public BaseReaderHandler<UTF8<>, PolicyParser> {
     if (s.empty()) {
       return false;
     }
-
     return s.back().do_string(cct, str, length);
   }
   bool RawNumber(const char* str, SizeType length, bool copy) {
@@ -638,13 +668,24 @@ bool ParseState::obj_end() {
 }
 
 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;
@@ -654,7 +695,6 @@ bool ParseState::key(const char* s, size_t l) {
   // 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
@@ -673,6 +713,7 @@ bool ParseState::key(const char* s, size_t l) {
   } 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;
@@ -681,7 +722,7 @@ bool ParseState::key(const char* s, size_t l) {
 // 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();
@@ -691,8 +732,27 @@ static optional<Principal> parse_principal(CephContext* cct, TokenID t,
 
     // 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 == '/');
@@ -703,28 +763,6 @@ static optional<Principal> parse_principal(CephContext* cct, TokenID t,
        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;
@@ -747,7 +785,7 @@ bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
 
   } 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 == '*') {
@@ -757,7 +795,7 @@ bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
   } 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;
       }
     }
@@ -781,13 +819,16 @@ bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
     // 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
 
@@ -822,9 +863,7 @@ bool ParseState::number(const char* s, size_t l) {
 }
 
 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() {
@@ -853,14 +892,14 @@ bool ParseState::array_end() {
 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 {
@@ -870,13 +909,13 @@ ostream& operator <<(ostream& m, const MaskedIP& ip) {
       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;
 }
@@ -894,7 +933,7 @@ bool Condition::eval(const Environment& env) const {
   }
 
   if (i == env.end()) {
-    return false;
+    return ifexists;
   }
   const auto& s = i->second;
 
@@ -904,28 +943,27 @@ bool Condition::eval(const Environment& env) const {
     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);
 
 
@@ -947,7 +985,7 @@ bool Condition::eval(const Environment& env) const {
     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:
@@ -978,8 +1016,24 @@ bool Condition::eval(const Environment& env) const {
     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?)
@@ -998,7 +1052,7 @@ optional<MaskedIP> Condition::as_network(const string& s) {
     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;
@@ -1020,36 +1074,37 @@ optional<MaskedIP> Condition::as_network(const string& s) {
   }
 
   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 {
@@ -1356,6 +1411,24 @@ const char* action_bit_string(uint64_t action) {
 
   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";
 }