|
| 1 | +From 1c1e599376c5db9321c660d6ac5ba6c99fb71c38 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Azure Linux Security Servicing Account |
| 3 | + |
| 4 | +Date: Tue, 1 Jul 2025 10:47:03 +0000 |
| 5 | +Subject: [PATCH] Fix CVE CVE-2025-52555 in ceph |
| 6 | + |
| 7 | +An unprivileged user can `chmod 777` a directory owned by root |
| 8 | +and gain access. Fix this bug and also add a test case for the |
| 9 | +same. |
| 10 | + |
| 11 | +Signed-off-by: Xiubo Li < [email protected]> |
| 12 | +Signed-off-by: Venky Shankar < [email protected]> |
| 13 | + |
| 14 | +Upstream Patch Reference: https://patch-diff.githubusercontent.com/raw/ceph/ceph/pull/60314.patch |
| 15 | + |
| 16 | +--- |
| 17 | + src/client/Client.cc | 24 ++++++++++++++---------- |
| 18 | + src/test/libcephfs/suidsgid.cc | 10 ++++++++++ |
| 19 | + 2 files changed, 24 insertions(+), 10 deletions(-) |
| 20 | + |
| 21 | +diff --git a/src/client/Client.cc b/src/client/Client.cc |
| 22 | +index 2b7db5a89..eca980a25 100644 |
| 23 | +--- a/src/client/Client.cc |
| 24 | ++++ b/src/client/Client.cc |
| 25 | +@@ -5949,18 +5949,22 @@ int Client::may_setattr(Inode *in, struct ceph_statx *stx, int mask, |
| 26 | + } |
| 27 | + |
| 28 | + if (mask & CEPH_SETATTR_MODE) { |
| 29 | ++ bool allowed = false; |
| 30 | ++ /* |
| 31 | ++ * Currently the kernel fuse and libfuse code is buggy and |
| 32 | ++ * won't pass the ATTR_KILL_SUID/ATTR_KILL_SGID to ceph-fuse. |
| 33 | ++ * But will just set the ATTR_MODE and at the same time by |
| 34 | ++ * clearing the suid/sgid bits. |
| 35 | ++ * |
| 36 | ++ * Only allow unprivileged users to clear S_ISUID and S_ISUID. |
| 37 | ++ */ |
| 38 | ++ if ((in->mode & (S_ISUID | S_ISGID)) != (stx->stx_mode & (S_ISUID | S_ISGID)) && |
| 39 | ++ (in->mode & ~(S_ISUID | S_ISGID)) == (stx->stx_mode & ~(S_ISUID | S_ISGID))) { |
| 40 | ++ allowed = true; |
| 41 | ++ } |
| 42 | + uint32_t m = ~stx->stx_mode & in->mode; // mode bits removed |
| 43 | + ldout(cct, 20) << __func__ << " " << *in << " = " << hex << m << dec << dendl; |
| 44 | +- if (perms.uid() != 0 && perms.uid() != in->uid && |
| 45 | +- /* |
| 46 | +- * Currently the kernel fuse and libfuse code is buggy and |
| 47 | +- * won't pass the ATTR_KILL_SUID/ATTR_KILL_SGID to ceph-fuse. |
| 48 | +- * But will just set the ATTR_MODE and at the same time by |
| 49 | +- * clearing the suid/sgid bits. |
| 50 | +- * |
| 51 | +- * Only allow unprivileged users to clear S_ISUID and S_ISUID. |
| 52 | +- */ |
| 53 | +- (m & ~(S_ISUID | S_ISGID))) |
| 54 | ++ if (perms.uid() != 0 && perms.uid() != in->uid && !allowed) |
| 55 | + goto out; |
| 56 | + |
| 57 | + gid_t i_gid = (mask & CEPH_SETATTR_GID) ? stx->stx_gid : in->gid; |
| 58 | +diff --git a/src/test/libcephfs/suidsgid.cc b/src/test/libcephfs/suidsgid.cc |
| 59 | +index d750613eb..474795cc4 100644 |
| 60 | +--- a/src/test/libcephfs/suidsgid.cc |
| 61 | ++++ b/src/test/libcephfs/suidsgid.cc |
| 62 | +@@ -134,6 +134,14 @@ void run_truncate_test_case(int mode, int result, size_t size, bool with_admin=f |
| 63 | + ceph_close(_cmount, fd); |
| 64 | + } |
| 65 | + |
| 66 | ++void run_change_mode_test_case() |
| 67 | ++{ |
| 68 | ++ char c_dir[1024]; |
| 69 | ++ sprintf(c_dir, "/mode_test_%d", getpid()); |
| 70 | ++ ASSERT_EQ(0, ceph_mkdirs(admin, c_dir, 0700)); |
| 71 | ++ ASSERT_EQ(ceph_chmod(cmount, c_dir, 0777), -CEPHFS_EPERM); |
| 72 | ++} |
| 73 | ++ |
| 74 | + TEST(SuidsgidTest, WriteClearSetuid) { |
| 75 | + ASSERT_EQ(0, ceph_create(&admin, NULL)); |
| 76 | + ASSERT_EQ(0, ceph_conf_read_file(admin, NULL)); |
| 77 | +@@ -206,6 +214,8 @@ TEST(SuidsgidTest, WriteClearSetuid) { |
| 78 | + // 14, Truncate by unprivileged user clears the suid and sgid |
| 79 | + run_truncate_test_case(06766, 0, 100); |
| 80 | + |
| 81 | ++ run_change_mode_test_case(); |
| 82 | ++ |
| 83 | + // clean up |
| 84 | + ceph_shutdown(cmount); |
| 85 | + ceph_shutdown(admin); |
| 86 | +-- |
| 87 | +2.45.3 |
| 88 | + |
0 commit comments