]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
client: disallow unprivileged users to escalate root privileges 62025/head
authorXiubo Li <xiubli@redhat.com>
Wed, 3 Apr 2024 11:02:08 +0000 (19:02 +0800)
committerVenky Shankar <vshankar@redhat.com>
Thu, 8 May 2025 21:30:58 +0000 (03:00 +0530)
An unprivileged user can `chmod 777` a directory owned by root
and gain access. Fix this bug and also add a test case for the
same.

Signed-off-by: Xiubo Li <xiubli@redhat.com>
Signed-off-by: Venky Shankar <vshankar@redhat.com>
src/client/Client.cc
src/test/libcephfs/suidsgid.cc

index f1f70c1321d81572aceb3a0c249af558ec511bf3..eefcf555dc9b8152af4e952b6c2c652d451f5292 100644 (file)
@@ -6204,18 +6204,22 @@ int Client::may_setattr(const InodeRef& in, struct ceph_statx *stx, int mask,
   }
 
   if (mask & CEPH_SETATTR_MODE) {
+    bool allowed = false;
+    /*
+     * Currently the kernel fuse and libfuse code is buggy and
+     * won't pass the ATTR_KILL_SUID/ATTR_KILL_SGID to ceph-fuse.
+     * But will just set the ATTR_MODE and at the same time by
+     * clearing the suid/sgid bits.
+     *
+     * Only allow unprivileged users to clear S_ISUID and S_ISUID.
+     */
+    if ((in->mode & (S_ISUID | S_ISGID)) != (stx->stx_mode & (S_ISUID | S_ISGID)) &&
+        (in->mode & ~(S_ISUID | S_ISGID)) == (stx->stx_mode & ~(S_ISUID | S_ISGID))) {
+      allowed = true;
+    }
     uint32_t m = ~stx->stx_mode & in->mode; // mode bits removed
     ldout(cct, 20) << __func__ << " " << *in << " = " << hex << m << dec <<  dendl;
-    if (perms.uid() != 0 && perms.uid() != in->uid &&
-       /*
-        * Currently the kernel fuse and libfuse code is buggy and
-        * won't pass the ATTR_KILL_SUID/ATTR_KILL_SGID to ceph-fuse.
-        * But will just set the ATTR_MODE and at the same time by
-        * clearing the suid/sgid bits.
-        *
-        * Only allow unprivileged users to clear S_ISUID and S_ISUID.
-        */
-       (m & ~(S_ISUID | S_ISGID)))
+    if (perms.uid() != 0 && perms.uid() != in->uid && !allowed)
       goto out;
 
     gid_t i_gid = (mask & CEPH_SETATTR_GID) ? stx->stx_gid : in->gid;
index 0802e8cb2b40a61678b0e437316f48dccee7f2b1..2ca38349a720a155ee405aa29ecda1c998a266b3 100644 (file)
@@ -134,6 +134,14 @@ void run_truncate_test_case(int mode, int result, size_t size, bool with_admin=f
   ceph_close(_cmount, fd);
 }
 
+void run_change_mode_test_case()
+{
+  char c_dir[1024];
+  sprintf(c_dir, "/mode_test_%d", getpid());
+  ASSERT_EQ(0, ceph_mkdirs(admin, c_dir, 0700));
+  ASSERT_EQ(ceph_chmod(cmount, c_dir, 0777), -EPERM);
+}
+
 TEST(SuidsgidTest, WriteClearSetuid) {
   ASSERT_EQ(0, ceph_create(&admin, NULL));
   ASSERT_EQ(0, ceph_conf_read_file(admin, NULL));
@@ -206,6 +214,8 @@ TEST(SuidsgidTest, WriteClearSetuid) {
   // 14, Truncate by unprivileged user clears the suid and sgid
   run_truncate_test_case(06766, 0, 100);
 
+  run_change_mode_test_case();
+
   // clean up
   ceph_shutdown(cmount);
   ceph_shutdown(admin);