gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/uidgid.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <errno.h> 16 #include <grp.h> 17 #include <sys/resource.h> 18 #include <sys/types.h> 19 #include <unistd.h> 20 21 #include "gtest/gtest.h" 22 #include "absl/flags/flag.h" 23 #include "absl/strings/str_cat.h" 24 #include "absl/strings/str_join.h" 25 #include "test/util/capability_util.h" 26 #include "test/util/cleanup.h" 27 #include "test/util/multiprocess_util.h" 28 #include "test/util/posix_error.h" 29 #include "test/util/test_util.h" 30 #include "test/util/thread_util.h" 31 #include "test/util/uid_util.h" 32 33 ABSL_FLAG(int32_t, scratch_uid1, 65534, "first scratch UID"); 34 ABSL_FLAG(int32_t, scratch_uid2, 65533, "second scratch UID"); 35 ABSL_FLAG(int32_t, scratch_gid1, 65534, "first scratch GID"); 36 ABSL_FLAG(int32_t, scratch_gid2, 65533, "second scratch GID"); 37 38 // Force use of syscall instead of glibc set*id() wrappers because we want to 39 // apply to the current task only. libc sets all threads in a process because 40 // "POSIX requires that all threads in a process share the same credentials." 41 #define setuid USE_SYSCALL_INSTEAD 42 #define setgid USE_SYSCALL_INSTEAD 43 #define setreuid USE_SYSCALL_INSTEAD 44 #define setregid USE_SYSCALL_INSTEAD 45 #define setresuid USE_SYSCALL_INSTEAD 46 #define setresgid USE_SYSCALL_INSTEAD 47 48 using ::testing::UnorderedElementsAreArray; 49 50 namespace gvisor { 51 namespace testing { 52 53 namespace { 54 55 TEST(UidGidTest, Getuid) { 56 uid_t ruid, euid, suid; 57 EXPECT_THAT(getresuid(&ruid, &euid, &suid), SyscallSucceeds()); 58 EXPECT_THAT(getuid(), SyscallSucceedsWithValue(ruid)); 59 EXPECT_THAT(geteuid(), SyscallSucceedsWithValue(euid)); 60 } 61 62 TEST(UidGidTest, Getgid) { 63 gid_t rgid, egid, sgid; 64 EXPECT_THAT(getresgid(&rgid, &egid, &sgid), SyscallSucceeds()); 65 EXPECT_THAT(getgid(), SyscallSucceedsWithValue(rgid)); 66 EXPECT_THAT(getegid(), SyscallSucceedsWithValue(egid)); 67 } 68 69 TEST(UidGidTest, Getgroups) { 70 // "If size is zero, list is not modified, but the total number of 71 // supplementary group IDs for the process is returned." - getgroups(2) 72 int nr_groups; 73 ASSERT_THAT(nr_groups = getgroups(0, nullptr), SyscallSucceeds()); 74 std::vector<gid_t> list(nr_groups); 75 EXPECT_THAT(getgroups(list.size(), list.data()), SyscallSucceeds()); 76 77 // "EINVAL: size is less than the number of supplementary group IDs, but is 78 // not zero." 79 #pragma GCC diagnostic push 80 #pragma GCC diagnostic ignored "-Wstringop-overflow=" 81 EXPECT_THAT(getgroups(-1, nullptr), SyscallFailsWithErrno(EINVAL)); 82 #pragma GCC diagnostic pop 83 84 // Testing for EFAULT requires actually having groups, which isn't guaranteed 85 // here; see the setgroups test below. 86 } 87 88 // Checks that the calling process' real/effective/saved user IDs are 89 // ruid/euid/suid respectively. 90 PosixError CheckUIDs(uid_t ruid, uid_t euid, uid_t suid) { 91 uid_t actual_ruid, actual_euid, actual_suid; 92 int rc = getresuid(&actual_ruid, &actual_euid, &actual_suid); 93 MaybeSave(); 94 if (rc < 0) { 95 return PosixError(errno, "getresuid"); 96 } 97 if (ruid != actual_ruid || euid != actual_euid || suid != actual_suid) { 98 return PosixError( 99 EPERM, absl::StrCat( 100 "incorrect user IDs: got (", 101 absl::StrJoin({actual_ruid, actual_euid, actual_suid}, ", "), 102 ", wanted (", absl::StrJoin({ruid, euid, suid}, ", "), ")")); 103 } 104 return NoError(); 105 } 106 107 PosixError CheckGIDs(gid_t rgid, gid_t egid, gid_t sgid) { 108 gid_t actual_rgid, actual_egid, actual_sgid; 109 int rc = getresgid(&actual_rgid, &actual_egid, &actual_sgid); 110 MaybeSave(); 111 if (rc < 0) { 112 return PosixError(errno, "getresgid"); 113 } 114 if (rgid != actual_rgid || egid != actual_egid || sgid != actual_sgid) { 115 return PosixError( 116 EPERM, absl::StrCat( 117 "incorrect group IDs: got (", 118 absl::StrJoin({actual_rgid, actual_egid, actual_sgid}, ", "), 119 ", wanted (", absl::StrJoin({rgid, egid, sgid}, ", "), ")")); 120 } 121 return NoError(); 122 } 123 124 // N.B. These tests may break horribly unless run via a gVisor test runner, 125 // because changing UID in one test may forfeit permissions required by other 126 // tests. (The test runner runs each test in a separate process.) 127 128 TEST(UidGidRootTest, Setuid) { 129 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 130 131 // Do setuid in a separate thread so that after finishing this test, the 132 // process can still open files the test harness created before starting this 133 // test. Otherwise, the files are created by root (UID before the test), but 134 // cannot be opened by the `uid` set below after the test. After calling 135 // setuid(non-zero-UID), there is no way to get root privileges back. 136 ScopedThread([&] { 137 // Use syscall instead of glibc setuid wrapper because we want this setuid 138 // call to only apply to this task. POSIX threads, however, require that all 139 // threads have the same UIDs, so using the setuid wrapper sets all threads' 140 // real UID. 141 EXPECT_THAT(syscall(SYS_setuid, -1), SyscallFailsWithErrno(EINVAL)); 142 143 const uid_t uid = absl::GetFlag(FLAGS_scratch_uid1); 144 EXPECT_THAT(syscall(SYS_setuid, uid), SyscallSucceeds()); 145 // "If the effective UID of the caller is root (more precisely: if the 146 // caller has the CAP_SETUID capability), the real UID and saved set-user-ID 147 // are also set." - setuid(2) 148 EXPECT_NO_ERRNO(CheckUIDs(uid, uid, uid)); 149 }); 150 } 151 152 TEST(UidGidRootTest, Setgid) { 153 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 154 155 EXPECT_THAT(syscall(SYS_setgid, -1), SyscallFailsWithErrno(EINVAL)); 156 157 ScopedThread([&] { 158 const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1); 159 EXPECT_THAT(syscall(SYS_setgid, gid), SyscallSucceeds()); 160 EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); 161 }); 162 } 163 164 TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) { 165 #pragma push_macro("allow_setgid") 166 #undef setgid 167 168 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 169 170 int old_gid = getgid(); 171 auto clean = Cleanup([old_gid] { setgid(old_gid); }); 172 173 const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1); 174 // NOTE(b/64676707): Do setgid in a separate thread so that we can test if 175 // info.si_pid is set correctly. 176 ScopedThread thread = 177 ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); }); 178 thread.Join(); 179 EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); 180 181 #pragma pop_macro("allow_setgid") 182 } 183 184 TEST(UidGidRootTest, Setreuid) { 185 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 186 187 // "Supplying a value of -1 for either the real or effective user ID forces 188 // the system to leave that ID unchanged." - setreuid(2) 189 EXPECT_THAT(syscall(SYS_setreuid, -1, -1), SyscallSucceeds()); 190 191 EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); 192 193 // Do setuid in a separate thread so that after finishing this test, the 194 // process can still open files the test harness created before starting 195 // this test. Otherwise, the files are created by root (UID before the 196 // test), but cannot be opened by the `uid` set below after the test. After 197 // calling setuid(non-zero-UID), there is no way to get root privileges 198 // back. 199 ScopedThread([&] { 200 const uid_t ruid = absl::GetFlag(FLAGS_scratch_uid1); 201 const uid_t euid = absl::GetFlag(FLAGS_scratch_uid2); 202 203 EXPECT_THAT(syscall(SYS_setreuid, ruid, euid), SyscallSucceeds()); 204 205 // "If the real user ID is set or the effective user ID is set to a value 206 // not equal to the previous real user ID, the saved set-user-ID will be 207 // set to the new effective user ID." - setreuid(2) 208 EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, euid)); 209 }); 210 } 211 212 TEST(UidGidRootTest, Setregid) { 213 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 214 215 EXPECT_THAT(syscall(SYS_setregid, -1, -1), SyscallSucceeds()); 216 EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); 217 218 ScopedThread([&] { 219 const gid_t rgid = absl::GetFlag(FLAGS_scratch_gid1); 220 const gid_t egid = absl::GetFlag(FLAGS_scratch_gid2); 221 ASSERT_THAT(syscall(SYS_setregid, rgid, egid), SyscallSucceeds()); 222 EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, egid)); 223 }); 224 } 225 226 TEST(UidGidRootTest, Setresuid) { 227 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 228 229 // "If one of the arguments equals -1, the corresponding value is not 230 // changed." - setresuid(2) 231 EXPECT_THAT(syscall(SYS_setresuid, -1, -1, -1), SyscallSucceeds()); 232 EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); 233 234 // Do setuid in a separate thread so that after finishing this test, the 235 // process can still open files the test harness created before starting 236 // this test. Otherwise, the files are created by root (UID before the 237 // test), but cannot be opened by the `uid` set below after the test. After 238 // calling setuid(non-zero-UID), there is no way to get root privileges 239 // back. 240 ScopedThread([&] { 241 const uid_t ruid = 12345; 242 const uid_t euid = 23456; 243 const uid_t suid = 34567; 244 245 // Use syscall instead of glibc setuid wrapper because we want this setuid 246 // call to only apply to this task. posix threads, however, require that 247 // all threads have the same UIDs, so using the setuid wrapper sets all 248 // threads' real UID. 249 EXPECT_THAT(syscall(SYS_setresuid, ruid, euid, suid), SyscallSucceeds()); 250 EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, suid)); 251 }); 252 } 253 254 TEST(UidGidRootTest, Setresgid) { 255 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 256 257 EXPECT_THAT(syscall(SYS_setresgid, -1, -1, -1), SyscallSucceeds()); 258 EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); 259 260 ScopedThread([&] { 261 const gid_t rgid = 12345; 262 const gid_t egid = 23456; 263 const gid_t sgid = 34567; 264 ASSERT_THAT(syscall(SYS_setresgid, rgid, egid, sgid), SyscallSucceeds()); 265 EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, sgid)); 266 }); 267 } 268 269 TEST(UidGidRootTest, Setgroups) { 270 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 271 272 std::vector<gid_t> list = {123, 500}; 273 ASSERT_THAT(setgroups(list.size(), list.data()), SyscallSucceeds()); 274 std::vector<gid_t> list2(list.size()); 275 ASSERT_THAT(getgroups(list2.size(), list2.data()), SyscallSucceeds()); 276 EXPECT_THAT(list, UnorderedElementsAreArray(list2)); 277 278 // "EFAULT: list has an invalid address." 279 EXPECT_THAT(getgroups(100, reinterpret_cast<gid_t*>(-1)), 280 SyscallFailsWithErrno(EFAULT)); 281 } 282 283 TEST(UidGidRootTest, Setuid_prlimit) { 284 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); 285 286 // Do seteuid in a separate thread so that after finishing this test, the 287 // process can still open files the test harness created before starting 288 // this test. Otherwise, the files are created by root (UID before the 289 // test), but cannot be opened by the `uid` set below after the test. 290 ScopedThread([&] { 291 // Use syscall instead of glibc setuid wrapper because we want this 292 // seteuid call to only apply to this task. POSIX threads, however, 293 // require that all threads have the same UIDs, so using the seteuid 294 // wrapper sets all threads' UID. 295 EXPECT_THAT(syscall(SYS_setreuid, -1, 65534), SyscallSucceeds()); 296 297 // Despite the UID change, we should be able to get our own limits. 298 struct rlimit rl = {}; 299 EXPECT_THAT(prlimit(0, RLIMIT_NOFILE, NULL, &rl), SyscallSucceeds()); 300 }); 301 } 302 303 } // namespace 304 305 } // namespace testing 306 } // namespace gvisor