From: Casey Bodley Date: Thu, 13 Mar 2025 19:14:39 +0000 (-0400) Subject: rgw/iam: add policy evaluation for Arn-based Conditions X-Git-Tag: v19.2.3~58^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=7bbd7318f1e81da274f5b78f4688a2ef5d068ce6;p=ceph.git 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) --- diff --git a/PendingReleaseNotes b/PendingReleaseNotes index e9c37a59e202..fb73dd1fe6f7 100644 --- a/PendingReleaseNotes +++ b/PendingReleaseNotes @@ -3,6 +3,8 @@ * RGW: Lua scripts will not run against health checks. * RGW: Adding missing quotes to the ETag values returned by S3 CopyPart, PostObject and CompleteMultipartUpload responses. +* RGW: IAM policy evaluation now supports conditions ArnEquals and ArnLike, along + with their Not and IfExists variants. * RBD: All Python APIs that produce timestamps now return "aware" `datetime` objects instead of "naive" ones (i.e. those including time zone information diff --git a/src/rgw/rgw_iam_policy.cc b/src/rgw/rgw_iam_policy.cc index bef4b587a666..f8d27ba62eb4 100644 --- a/src/rgw/rgw_iam_policy.cc +++ b/src/rgw/rgw_iam_policy.cc @@ -839,6 +839,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); @@ -979,11 +991,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 aa97fb876724..4267f184fd2a 100644 --- a/src/test/rgw/test_rgw_iam_policy.cc +++ b/src/test/rgw/test_rgw_iam_policy.cc @@ -49,6 +49,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::s3objectlambdaAll; using rgw::IAM::s3GetAccelerateConfiguration; @@ -1482,3 +1483,27 @@ TEST(set_cont_bits, iamconsts) EXPECT_EQ(organizationsAllValue, set_range_bits(snsAll+1, organizationsAll)); 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"}})); + } +}