From d578ab0439d7ff6feb732dd50e4d4ac1ef1339e6 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Thu, 13 Mar 2025 15:14:39 -0400 Subject: [PATCH] rgw/iam: add policy evaluation for Arn-based Conditions arn-based conditions like ArnEquals etc are recognized by the policy parser, but not enforced during policy evaluation Fixes: https://tracker.ceph.com/issues/70481 Signed-off-by: Casey Bodley (cherry picked from commit 62c3e5ec69f115c6991e632f6f27f4f295e3741a) --- PendingReleaseNotes | 2 ++ src/rgw/rgw_iam_policy.cc | 25 ++++++++++++++++++++----- src/test/rgw/test_rgw_iam_policy.cc | 25 +++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/PendingReleaseNotes b/PendingReleaseNotes index 6aaa4ffb30f57..c1fd3b941e046 100644 --- a/PendingReleaseNotes +++ b/PendingReleaseNotes @@ -19,6 +19,8 @@ Related trackers: - https://tracker.ceph.com/issues/67179 - https://tracker.ceph.com/issues/66867 +* RGW: IAM policy evaluation now supports conditions ArnEquals and ArnLike, along + with their Not and IfExists variants. >=18.2.4 -------- diff --git a/src/rgw/rgw_iam_policy.cc b/src/rgw/rgw_iam_policy.cc index cf56f0f557288..8f6bb5b83c26f 100644 --- a/src/rgw/rgw_iam_policy.cc +++ b/src/rgw/rgw_iam_policy.cc @@ -764,6 +764,18 @@ ostream& operator <<(ostream& m, const MaskedIP& ip) { return m; } +// Case-sensitive matching of the ARN. Each of the six colon-delimited +// components of the ARN is checked separately and each can include multi- +// character match wildcards (*) or single-character match wildcards (?). +static bool arn_like(const std::string& input, const std::string& pattern) +{ + constexpr auto delim = [] (char c) { return c == ':'; }; + if (std::count_if(input.begin(), input.end(), delim) != 5) { + return false; + } + return match_policy(pattern, input, MATCH_POLICY_ARN); +} + bool Condition::eval(const Environment& env) const { std::vector runtime_vals; auto i = env.find(key); @@ -904,11 +916,14 @@ bool Condition::eval(const Environment& env) const { return true; } -#if 0 - // Amazon Resource Names! (Does S3 need this?) - TokenID::ArnEquals, TokenID::ArnNotEquals, TokenID::ArnLike, - TokenID::ArnNotLike, -#endif + // Amazon Resource Names! + // The ArnEquals and ArnLike condition operators behave identically. + case TokenID::ArnEquals: + case TokenID::ArnLike: + return orrible(arn_like, itr, isruntime? runtime_vals : vals); + case TokenID::ArnNotEquals: + case TokenID::ArnNotLike: + return orrible(std::not_fn(arn_like), itr, isruntime? runtime_vals : vals); default: return false; diff --git a/src/test/rgw/test_rgw_iam_policy.cc b/src/test/rgw/test_rgw_iam_policy.cc index a79d20849da30..4f2467f0cc8bc 100644 --- a/src/test/rgw/test_rgw_iam_policy.cc +++ b/src/test/rgw/test_rgw_iam_policy.cc @@ -47,6 +47,7 @@ using rgw::IAM::Effect; using rgw::IAM::Environment; using rgw::Partition; using rgw::IAM::Policy; +using rgw::IAM::Condition; using rgw::IAM::s3All; using rgw::IAM::s3Count; using rgw::IAM::s3GetAccelerateConfiguration; @@ -1300,3 +1301,27 @@ TEST(set_cont_bits, iamconsts) EXPECT_EQ(stsAllValue, set_range_bits(iamAll+1, stsAll)); EXPECT_EQ(allValue , set_range_bits(0, allCount)); } + +TEST(Condition, ArnLike) +{ + const std::string key = "aws:SourceArn"; + { + Condition ArnLike{TokenID::ArnLike, key.data(), key.size(), false}; + ArnLike.vals.push_back("arn:aws:s3:::bucket"); + + EXPECT_FALSE(ArnLike.eval({})); + EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}})); + EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}})); + EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}})); + } + { + Condition ArnLike{TokenID::ArnLike, key.data(), key.size(), false}; + ArnLike.vals.push_back("arn:aws:s3:::b*"); + + EXPECT_FALSE(ArnLike.eval({})); + EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::b"}})); + EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}})); + EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}})); + EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}})); + } +} -- 2.39.5