|
23 | 23 | #include "gtest/gtest.h" |
24 | 24 | #include "absl/flags/flag.h" |
25 | 25 | #include "absl/synchronization/notification.h" |
26 | | -#include "test/util/capability_util.h" |
27 | 26 | #include "test/util/file_descriptor.h" |
28 | 27 | #include "test/util/fs_util.h" |
| 28 | +#include "test/util/linux_capability_util.h" |
29 | 29 | #include "test/util/posix_error.h" |
30 | 30 | #include "test/util/temp_path.h" |
31 | 31 | #include "test/util/test_util.h" |
@@ -127,6 +127,59 @@ TEST(ChownTest, FchownatEmptyPath) { |
127 | 127 | ASSERT_THAT(fchownat(fd.get(), "", 0, 0, 0), SyscallFailsWithErrno(ENOENT)); |
128 | 128 | } |
129 | 129 |
|
| 130 | +TEST(ChownTest, SetGroupIdBitDeniedWithoutCapFsetIdOrRelevantGroup) { |
| 131 | + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETGID))); |
| 132 | + const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); |
| 133 | + AutoCapability cap(CAP_FSETID, false); |
| 134 | + |
| 135 | + // Changing task creds in the main thread can mess with other tests. |
| 136 | + ScopedThread([&] { |
| 137 | + const mode_t chown_mode = 0755 | S_ISGID; |
| 138 | + const gid_t oddGid = 12345; |
| 139 | + struct stat stats; |
| 140 | + mode_t stat_mode; |
| 141 | + |
| 142 | + // 1) Without CAP_FSETID, and no relevant group, S_ISGID is not applied. |
| 143 | + ASSERT_THAT(syscall(SYS_setresgid, oddGid, oddGid, oddGid), |
| 144 | + SyscallSucceeds()); |
| 145 | + std::vector<gid_t> no_extra_groups; |
| 146 | + ASSERT_THAT( |
| 147 | + syscall(SYS_setgroups, no_extra_groups.size(), no_extra_groups.data()), |
| 148 | + SyscallSucceeds()); |
| 149 | + |
| 150 | + ASSERT_THAT(chmod(file.path().c_str(), chown_mode), SyscallSucceeds()); |
| 151 | + ASSERT_THAT(stat(file.path().c_str(), &stats), SyscallSucceeds()); |
| 152 | + stat_mode = stats.st_mode & ~S_IFMT; |
| 153 | + |
| 154 | + EXPECT_EQ(stat_mode & S_ISGID, 0); // S_ISGID is not applied. |
| 155 | + EXPECT_EQ(stat_mode, chown_mode & ~S_ISGID); // but the rest is. |
| 156 | + |
| 157 | + // 2) Without CAP_FSETID, but with a relevant group, S_ISGID is applied. |
| 158 | + gid_t file_gid = stats.st_gid; |
| 159 | + ASSERT_THAT(syscall(SYS_setresgid, file_gid, file_gid, file_gid), |
| 160 | + SyscallSucceeds()); |
| 161 | + |
| 162 | + ASSERT_THAT(chmod(file.path().c_str(), chown_mode), SyscallSucceeds()); |
| 163 | + ASSERT_THAT(stat(file.path().c_str(), &stats), SyscallSucceeds()); |
| 164 | + stat_mode = stats.st_mode & ~S_IFMT; |
| 165 | + |
| 166 | + EXPECT_EQ(stat_mode & S_ISGID, S_ISGID); // S_ISGID is applied. |
| 167 | + EXPECT_EQ(stat_mode, chown_mode); // as is the rest. |
| 168 | + |
| 169 | + // 3) With CAP_FSETID, but without a relevant group, S_ISGID is applied. |
| 170 | + AutoCapability cap_inner(CAP_FSETID, true); |
| 171 | + ASSERT_THAT(syscall(SYS_setresgid, oddGid, oddGid, oddGid), |
| 172 | + SyscallSucceeds()); |
| 173 | + |
| 174 | + ASSERT_THAT(chmod(file.path().c_str(), chown_mode), SyscallSucceeds()); |
| 175 | + ASSERT_THAT(stat(file.path().c_str(), &stats), SyscallSucceeds()); |
| 176 | + stat_mode = stats.st_mode & ~S_IFMT; |
| 177 | + |
| 178 | + EXPECT_EQ(stat_mode & S_ISGID, S_ISGID); // S_ISGID is applied. |
| 179 | + EXPECT_EQ(stat_mode, chown_mode); // as is the rest. |
| 180 | + }); |
| 181 | +} |
| 182 | + |
130 | 183 | using Chown = |
131 | 184 | std::function<PosixError(const std::string&, uid_t owner, gid_t group)>; |
132 | 185 |
|
|
0 commit comments