Skip to content

Commit ce31042

Browse files
authored
Merge pull request ceph#62285 from cbodley/wip-rgw-iam-arnlike
rgw/iam: add policy evaluation for Arn-based Conditions Reviewed-by: Adam Emerson <[email protected]> Reviewed-by: Yuval Lifshitz <[email protected]>
2 parents 3233601 + 62c3e5e commit ce31042

File tree

6 files changed

+58
-40
lines changed

6 files changed

+58
-40
lines changed

PendingReleaseNotes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* RGW: For compatibility with AWS S3, LastModified timestamps are now truncated
1515
to the second. Note that during upgrade, users may observe these timestamps
1616
moving backwards as a result.
17+
* RGW: IAM policy evaluation now supports conditions ArnEquals and ArnLike, along
18+
with their Not and IfExists variants.
1719

1820
* RBD: All Python APIs that produce timestamps now return "aware" `datetime`
1921
objects instead of "naive" ones (i.e. those including time zone information

src/rgw/rgw_arn.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,15 +328,15 @@ bool ARN::match(const ARN& candidate) const {
328328
return false;
329329
}
330330

331-
if (!match_policy(region, candidate.region, MATCH_POLICY_ARN)) {
331+
if (!match_wildcards(region, candidate.region, MATCH_CASE_INSENSITIVE)) {
332332
return false;
333333
}
334334

335-
if (!match_policy(account, candidate.account, MATCH_POLICY_ARN)) {
335+
if (!match_wildcards(account, candidate.account, MATCH_CASE_INSENSITIVE)) {
336336
return false;
337337
}
338338

339-
if (!match_policy(resource, candidate.resource, MATCH_POLICY_RESOURCE)) {
339+
if (!match_wildcards(resource, candidate.resource, 0)) {
340340
return false;
341341
}
342342

src/rgw/rgw_common.cc

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,27 +2188,23 @@ int rgw_parse_op_type_list(const string& str, uint32_t *perm)
21882188
bool match_policy(const std::string& pattern, const std::string& input,
21892189
uint32_t flag)
21902190
{
2191-
const uint32_t flag2 = flag & (MATCH_POLICY_ACTION|MATCH_POLICY_ARN) ?
2191+
const uint32_t flag2 = (flag & MATCH_POLICY_ACTION) ?
21922192
MATCH_CASE_INSENSITIVE : 0;
2193-
const bool colonblocks = !(flag & (MATCH_POLICY_RESOURCE |
2194-
MATCH_POLICY_STRING));
21952193

2196-
const auto npos = std::string_view::npos;
21972194
std::string_view::size_type last_pos_input = 0, last_pos_pattern = 0;
21982195
while (true) {
2199-
auto cur_pos_input = colonblocks ? input.find(":", last_pos_input) : npos;
2200-
auto cur_pos_pattern =
2201-
colonblocks ? pattern.find(":", last_pos_pattern) : npos;
2196+
auto cur_pos_input = input.find(":", last_pos_input);
2197+
auto cur_pos_pattern = pattern.find(":", last_pos_pattern);
22022198

22032199
auto substr_input = input.substr(last_pos_input, cur_pos_input);
22042200
auto substr_pattern = pattern.substr(last_pos_pattern, cur_pos_pattern);
22052201

22062202
if (!match_wildcards(substr_pattern, substr_input, flag2))
22072203
return false;
22082204

2209-
if (cur_pos_pattern == npos)
2210-
return cur_pos_input == npos;
2211-
if (cur_pos_input == npos)
2205+
if (cur_pos_pattern == pattern.npos)
2206+
return cur_pos_input == input.npos;
2207+
if (cur_pos_input == input.npos)
22122208
return false;
22132209

22142210
last_pos_pattern = cur_pos_pattern + 1;

src/rgw/rgw_common.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,9 +1911,7 @@ extern std::string calc_hash_sha256_restart_stream(ceph::crypto::SHA256** phash)
19111911
extern int rgw_parse_op_type_list(const std::string& str, uint32_t *perm);
19121912

19131913
static constexpr uint32_t MATCH_POLICY_ACTION = 0x01;
1914-
static constexpr uint32_t MATCH_POLICY_RESOURCE = 0x02;
1915-
static constexpr uint32_t MATCH_POLICY_ARN = 0x04;
1916-
static constexpr uint32_t MATCH_POLICY_STRING = 0x08;
1914+
static constexpr uint32_t MATCH_POLICY_ARN = 0x02;
19171915

19181916
extern bool match_policy(const std::string& pattern, const std::string& input,
19191917
uint32_t flag);

src/rgw/rgw_iam_policy.cc

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,18 @@ ostream& operator <<(ostream& m, const MaskedIP& ip) {
852852
return m;
853853
}
854854

855+
// Case-sensitive matching of the ARN. Each of the six colon-delimited
856+
// components of the ARN is checked separately and each can include multi-
857+
// character match wildcards (*) or single-character match wildcards (?).
858+
static bool arn_like(const std::string& input, const std::string& pattern)
859+
{
860+
constexpr auto delim = [] (char c) { return c == ':'; };
861+
if (std::count_if(input.begin(), input.end(), delim) != 5) {
862+
return false;
863+
}
864+
return match_policy(pattern, input, MATCH_POLICY_ARN);
865+
}
866+
855867
bool Condition::eval(const Environment& env) const {
856868
std::vector<std::string> runtime_vals;
857869
auto i = env.find(key);
@@ -992,11 +1004,14 @@ bool Condition::eval(const Environment& env) const {
9921004
return true;
9931005
}
9941006

995-
#if 0
996-
// Amazon Resource Names! (Does S3 need this?)
997-
TokenID::ArnEquals, TokenID::ArnNotEquals, TokenID::ArnLike,
998-
TokenID::ArnNotLike,
999-
#endif
1007+
// Amazon Resource Names!
1008+
// The ArnEquals and ArnLike condition operators behave identically.
1009+
case TokenID::ArnEquals:
1010+
case TokenID::ArnLike:
1011+
return orrible(arn_like, itr, isruntime? runtime_vals : vals);
1012+
case TokenID::ArnNotEquals:
1013+
case TokenID::ArnNotLike:
1014+
return orrible(std::not_fn(arn_like), itr, isruntime? runtime_vals : vals);
10001015

10011016
default:
10021017
return false;

src/test/rgw/test_rgw_iam_policy.cc

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ using rgw::IAM::Effect;
4949
using rgw::IAM::Environment;
5050
using rgw::Partition;
5151
using rgw::IAM::Policy;
52+
using rgw::IAM::Condition;
5253
using rgw::IAM::s3All;
5354
using rgw::IAM::s3objectlambdaAll;
5455
using rgw::IAM::s3GetAccelerateConfiguration;
@@ -1469,31 +1470,13 @@ TEST(MatchPolicy, Action)
14691470
EXPECT_FALSE(match_policy("a:*", "a:b:c", flag)); // cannot span segments
14701471
}
14711472

1472-
TEST(MatchPolicy, Resource)
1473-
{
1474-
constexpr auto flag = MATCH_POLICY_RESOURCE;
1475-
EXPECT_TRUE(match_policy("a:b:c", "a:b:c", flag));
1476-
EXPECT_FALSE(match_policy("a:b:c", "A:B:C", flag)); // case sensitive
1477-
EXPECT_TRUE(match_policy("a:*:e", "a:bcd:e", flag));
1478-
EXPECT_TRUE(match_policy("a:*", "a:b:c", flag)); // can span segments
1479-
}
1480-
14811473
TEST(MatchPolicy, ARN)
14821474
{
14831475
constexpr auto flag = MATCH_POLICY_ARN;
14841476
EXPECT_TRUE(match_policy("a:b:c", "a:b:c", flag));
1485-
EXPECT_TRUE(match_policy("a:b:c", "A:B:C", flag)); // case insensitive
1486-
EXPECT_TRUE(match_policy("a:*:e", "a:bcd:e", flag));
1487-
EXPECT_FALSE(match_policy("a:*", "a:b:c", flag)); // cannot span segments
1488-
}
1489-
1490-
TEST(MatchPolicy, String)
1491-
{
1492-
constexpr auto flag = MATCH_POLICY_STRING;
1493-
EXPECT_TRUE(match_policy("a:b:c", "a:b:c", flag));
14941477
EXPECT_FALSE(match_policy("a:b:c", "A:B:C", flag)); // case sensitive
14951478
EXPECT_TRUE(match_policy("a:*:e", "a:bcd:e", flag));
1496-
EXPECT_TRUE(match_policy("a:*", "a:b:c", flag)); // can span segments
1479+
EXPECT_FALSE(match_policy("a:*", "a:b:c", flag)); // cannot span segments
14971480
}
14981481

14991482
Action_t set_range_bits(std::uint64_t start, std::uint64_t end)
@@ -1515,3 +1498,27 @@ TEST(set_cont_bits, iamconsts)
15151498
EXPECT_EQ(organizationsAllValue, set_range_bits(snsAll+1, organizationsAll));
15161499
EXPECT_EQ(allValue , set_range_bits(0, allCount));
15171500
}
1501+
1502+
TEST(Condition, ArnLike)
1503+
{
1504+
const std::string key = "aws:SourceArn";
1505+
{
1506+
Condition ArnLike{TokenID::ArnLike, key.data(), key.size(), false};
1507+
ArnLike.vals.push_back("arn:aws:s3:::bucket");
1508+
1509+
EXPECT_FALSE(ArnLike.eval({}));
1510+
EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}}));
1511+
EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}}));
1512+
EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}}));
1513+
}
1514+
{
1515+
Condition ArnLike{TokenID::ArnLike, key.data(), key.size(), false};
1516+
ArnLike.vals.push_back("arn:aws:s3:::b*");
1517+
1518+
EXPECT_FALSE(ArnLike.eval({}));
1519+
EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::b"}}));
1520+
EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}}));
1521+
EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}}));
1522+
EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}}));
1523+
}
1524+
}

0 commit comments

Comments
 (0)