Skip to content

Commit 7028ed2

Browse files
committed
client: prohibit unprivileged users from setting sgid/suid bits
Prior to fb1b72d, unprivileged users could add mode bits as long as S_ISUID and S_ISGID were not included in the change. After fb1b72d, unprivileged users were allowed to modify S_ISUID and S_ISGID bits only when no other mode bits were changed in the same operation. This inadvertently permitted unprivileged users to set S_ISUID and/or S_ISGID bits when they were the sole bits being modified. This behavior should not be allowed. Unprivileged users should be prohibited from setting S_ISUID and/or S_ISGID bits under any circumstances. This change tightens the permission check to prevent unprivileged users from setting these privileged bits in all cases. Signed-off-by: Kefu Chai <[email protected]>
1 parent 388cff0 commit 7028ed2

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

src/client/Client.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6204,22 +6204,23 @@ int Client::may_setattr(const InodeRef& in, struct ceph_statx *stx, int mask,
62046204
}
62056205

62066206
if (mask & CEPH_SETATTR_MODE) {
6207-
bool allowed = false;
62086207
/*
62096208
* Currently the kernel fuse and libfuse code is buggy and
62106209
* won't pass the ATTR_KILL_SUID/ATTR_KILL_SGID to ceph-fuse.
62116210
* But will just set the ATTR_MODE and at the same time by
62126211
* clearing the suid/sgid bits.
62136212
*
6214-
* Only allow unprivileged users to clear S_ISUID and S_ISUID.
6213+
* Only allow unprivileged users to clear S_ISUID and S_ISGID.
62156214
*/
6216-
if ((in->mode & (S_ISUID | S_ISGID)) != (stx->stx_mode & (S_ISUID | S_ISGID)) &&
6217-
(in->mode & ~(S_ISUID | S_ISGID)) == (stx->stx_mode & ~(S_ISUID | S_ISGID))) {
6218-
allowed = true;
6219-
}
6220-
uint32_t m = ~stx->stx_mode & in->mode; // mode bits removed
6221-
ldout(cct, 20) << __func__ << " " << *in << " = " << hex << m << dec << dendl;
6222-
if (perms.uid() != 0 && perms.uid() != in->uid && !allowed)
6215+
uint32_t removed_bits = ~stx->stx_mode & in->mode;
6216+
uint32_t added_bits = ~in->mode & stx->stx_mode;
6217+
bool clearing_suid_sgid = (
6218+
// no new bits added
6219+
added_bits == 0 &&
6220+
// only suid/suid bits removed
6221+
(removed_bits & ~(S_ISUID | S_ISGID)) == 0);
6222+
ldout(cct, 20) << __func__ << " " << *in << " = " << hex << removed_bits << dec << dendl;
6223+
if (perms.uid() != 0 && perms.uid() != in->uid && !clearing_suid_sgid)
62236224
goto out;
62246225

62256226
gid_t i_gid = (mask & CEPH_SETATTR_GID) ? stx->stx_gid : in->gid;

src/test/libcephfs/suidsgid.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "include/stringify.h"
2020
#include "include/cephfs/libcephfs.h"
2121
#include "include/rados/librados.h"
22+
#include <cerrno>
2223
#include <errno.h>
2324
#include <fcntl.h>
2425
#include <unistd.h>
@@ -142,6 +143,17 @@ void run_change_mode_test_case()
142143
ASSERT_EQ(ceph_chmod(cmount, c_dir, 0777), -EPERM);
143144
}
144145

146+
static void run_set_sgid_suid_test_case(int old_suid_sgid,
147+
int new_suid_sgid,
148+
int expected_result)
149+
{
150+
char c_dir[1024];
151+
sprintf(c_dir, "/mode_test_%d", getpid());
152+
const int mode = 0766;
153+
ASSERT_EQ(ceph_mkdirs(admin, c_dir, mode | old_suid_sgid), 0);
154+
ASSERT_EQ(ceph_chmod(cmount, c_dir, mode | new_suid_sgid), expected_result);
155+
}
156+
145157
TEST(SuidsgidTest, WriteClearSetuid) {
146158
ASSERT_EQ(0, ceph_create(&admin, NULL));
147159
ASSERT_EQ(0, ceph_conf_read_file(admin, NULL));
@@ -214,8 +226,18 @@ TEST(SuidsgidTest, WriteClearSetuid) {
214226
// 14, Truncate by unprivileged user clears the suid and sgid
215227
run_truncate_test_case(06766, 0, 100);
216228

229+
// 15, Prohibit unpriviledged user from changing non-sgid/suid
230+
// mode bits
217231
run_change_mode_test_case();
218232

233+
// 16, Prohibit unpriviledged user from setting suid/sgid
234+
run_set_sgid_suid_test_case(0, S_ISUID, -EPERM);
235+
run_set_sgid_suid_test_case(0, S_ISGID, -EPERM);
236+
run_set_sgid_suid_test_case(0, S_ISUID | S_ISGID, -EPERM);
237+
run_set_sgid_suid_test_case(S_ISGID, S_ISUID, -EPERM);
238+
run_set_sgid_suid_test_case(S_ISUID, S_ISGID, -EPERM);
239+
run_set_sgid_suid_test_case(S_ISGID, S_ISUID | S_ISGID, -EPERM);
240+
219241
// clean up
220242
ceph_shutdown(cmount);
221243
ceph_shutdown(admin);

0 commit comments

Comments
 (0)