CVE-2017-16818: Reachable Assertion (CWE-617)
value: RADOS Gateway in Ceph 12.1.0 through 12.2.1 allows remote authenticated users to cause a denial of service (assertion failure and application exit) by leveraging "full" (not necessarily admin) privileges to post an invalid profile to the admin API, related to rgw/rgw_iam_policy.cc, rgw/rgw_basic_types.h, and rgw/rgw_iam_types.h.
CWE description: The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary.
CVSS v3 base score: 6.5
Published: 2017-12-20T17:29Z
Repository: https://github.com/ceph/ceph
Language: C
Files changed: src/rgw/rgw_basic_types.h, src/rgw/rgw_iam_policy.cc, src/rgw/rgw_iam_policy.h
Commit date: 2017-10-10 16:21:48 -0400
Commit message: rgw: Remove assertions in IAM Policy

A couple of them could be triggered by user input.

Signed-off-by: Adam C. Emerson <aemerson@redhat.com>

Diff: diff --git a/src/rgw/rgw_basic_types.h b/src/rgw/rgw_basic_types.h
index 31e9d3a32a..b4707b1a70 100644
--- a/src/rgw/rgw_basic_types.h
+++ b/src/rgw/rgw_basic_types.h
@@ -158,16 +158,14 @@ public:
   bool is_tenant() const {
     return t == Tenant;
   }
 
   const std::string& get_tenant() const {
-    ceph_assert(t != Wildcard);
     return u.tenant;
   }
 
   const std::string& get_id() const {
-    ceph_assert(t != Wildcard && t != Tenant);
     return u.id;
   }
 
   bool operator ==(const Principal& o) const {
     return (t == o.t) && (u == o.u);
diff --git a/src/rgw/rgw_iam_policy.cc b/src/rgw/rgw_iam_policy.cc
index 2f6d8be81c..97d9c13b48 100644
--- a/src/rgw/rgw_iam_policy.cc
+++ b/src/rgw/rgw_iam_policy.cc
@@ -219,11 +219,13 @@ optional<ARN> ARN::parse(const string& s, bool wildcards) {
   smatch match;
 
   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);
+    if (match.size() != 6) {
+      return boost::none;
+    }
 
     ARN a;
     {
       auto p = to_partition(match[1], wildcards);
       if (!p)
@@ -769,11 +771,13 @@ static optional<Principal> parse_principal(CephContext* cct, TokenID t,
     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() == 3);
+      if (match.size() != 3) {
+	return boost::none;
+      }
 
       if (match[1] == "user") {
 	return Principal::user(std::move(a->account),
 			       match[2]);
       }
@@ -841,11 +845,13 @@ bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
     t.conditions.back().vals.emplace_back(s, 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)
diff --git a/src/rgw/rgw_iam_policy.h b/src/rgw/rgw_iam_policy.h
index 5236e6b021..59117456e0 100644
--- a/src/rgw/rgw_iam_policy.h
+++ b/src/rgw/rgw_iam_policy.h
@@ -27,12 +27,10 @@
 #include "rgw_acl.h"
 #include "rgw_basic_types.h"
 #include "rgw_iam_policy_keywords.h"
 #include "rgw_string.h"
 
-#include "include/assert.h" // razzin' frazzin' ...grrr.
-
 class RGWRados;
 namespace rgw {
 namespace auth {
 class Identity;
 }
@@ -252,11 +250,10 @@ std::ostream& operator <<(std::ostream& m, const MaskedIP& ip);
 string to_string(const MaskedIP& m);
 
 inline bool operator ==(const MaskedIP& l, const MaskedIP& r) {
   auto shift = std::max((l.v6 ? 128 : 32) - l.prefix,
 			(r.v6 ? 128 : 32) - r.prefix);
-  ceph_assert(shift > 0);
   return (l.addr >> shift) == (r.addr >> shift);
 }
 
 struct Condition {
   TokenID op;

Vulnerable code:     ceph_assert(t != Wildcard);
    ceph_assert(t != Wildcard && t != Tenant);
    ceph_assert(match.size() == 6);
      ceph_assert(match.size() == 3);
    ceph_assert(pp->s.size() > 1);
#include "include/assert.h" // razzin' frazzin' ...grrr.

  ceph_assert(shift > 0);
Fixed code:     if (match.size() != 6) {
      return boost::none;
    }
      if (match.size() != 3) {
	return boost::none;
      }
    if (pp->s.size() <= 1) {
      return false;
    }