diff options
Diffstat (limited to 'test/syscalls/linux/uidgid.cc')
-rw-r--r-- | test/syscalls/linux/uidgid.cc | 301 |
1 files changed, 0 insertions, 301 deletions
diff --git a/test/syscalls/linux/uidgid.cc b/test/syscalls/linux/uidgid.cc deleted file mode 100644 index 4139a18d8..000000000 --- a/test/syscalls/linux/uidgid.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <grp.h> -#include <sys/resource.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/uid_util.h" - -ABSL_FLAG(int32_t, scratch_uid1, 65534, "first scratch UID"); -ABSL_FLAG(int32_t, scratch_uid2, 65533, "second scratch UID"); -ABSL_FLAG(int32_t, scratch_gid1, 65534, "first scratch GID"); -ABSL_FLAG(int32_t, scratch_gid2, 65533, "second scratch GID"); - -// Force use of syscall instead of glibc set*id() wrappers because we want to -// apply to the current task only. libc sets all threads in a process because -// "POSIX requires that all threads in a process share the same credentials." -#define setuid USE_SYSCALL_INSTEAD -#define setgid USE_SYSCALL_INSTEAD -#define setreuid USE_SYSCALL_INSTEAD -#define setregid USE_SYSCALL_INSTEAD -#define setresuid USE_SYSCALL_INSTEAD -#define setresgid USE_SYSCALL_INSTEAD - -using ::testing::UnorderedElementsAreArray; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(UidGidTest, Getuid) { - uid_t ruid, euid, suid; - EXPECT_THAT(getresuid(&ruid, &euid, &suid), SyscallSucceeds()); - EXPECT_THAT(getuid(), SyscallSucceedsWithValue(ruid)); - EXPECT_THAT(geteuid(), SyscallSucceedsWithValue(euid)); -} - -TEST(UidGidTest, Getgid) { - gid_t rgid, egid, sgid; - EXPECT_THAT(getresgid(&rgid, &egid, &sgid), SyscallSucceeds()); - EXPECT_THAT(getgid(), SyscallSucceedsWithValue(rgid)); - EXPECT_THAT(getegid(), SyscallSucceedsWithValue(egid)); -} - -TEST(UidGidTest, Getgroups) { - // "If size is zero, list is not modified, but the total number of - // supplementary group IDs for the process is returned." - getgroups(2) - int nr_groups; - ASSERT_THAT(nr_groups = getgroups(0, nullptr), SyscallSucceeds()); - std::vector<gid_t> list(nr_groups); - EXPECT_THAT(getgroups(list.size(), list.data()), SyscallSucceeds()); - - // "EINVAL: size is less than the number of supplementary group IDs, but is - // not zero." - EXPECT_THAT(getgroups(-1, nullptr), SyscallFailsWithErrno(EINVAL)); - - // Testing for EFAULT requires actually having groups, which isn't guaranteed - // here; see the setgroups test below. -} - -// Checks that the calling process' real/effective/saved user IDs are -// ruid/euid/suid respectively. -PosixError CheckUIDs(uid_t ruid, uid_t euid, uid_t suid) { - uid_t actual_ruid, actual_euid, actual_suid; - int rc = getresuid(&actual_ruid, &actual_euid, &actual_suid); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "getresuid"); - } - if (ruid != actual_ruid || euid != actual_euid || suid != actual_suid) { - return PosixError( - EPERM, absl::StrCat( - "incorrect user IDs: got (", - absl::StrJoin({actual_ruid, actual_euid, actual_suid}, ", "), - ", wanted (", absl::StrJoin({ruid, euid, suid}, ", "), ")")); - } - return NoError(); -} - -PosixError CheckGIDs(gid_t rgid, gid_t egid, gid_t sgid) { - gid_t actual_rgid, actual_egid, actual_sgid; - int rc = getresgid(&actual_rgid, &actual_egid, &actual_sgid); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "getresgid"); - } - if (rgid != actual_rgid || egid != actual_egid || sgid != actual_sgid) { - return PosixError( - EPERM, absl::StrCat( - "incorrect group IDs: got (", - absl::StrJoin({actual_rgid, actual_egid, actual_sgid}, ", "), - ", wanted (", absl::StrJoin({rgid, egid, sgid}, ", "), ")")); - } - return NoError(); -} - -// N.B. These tests may break horribly unless run via a gVisor test runner, -// because changing UID in one test may forfeit permissions required by other -// tests. (The test runner runs each test in a separate process.) - -TEST(UidGidRootTest, Setuid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting this - // test. Otherwise, the files are created by root (UID before the test), but - // cannot be opened by the `uid` set below after the test. After calling - // setuid(non-zero-UID), there is no way to get root privileges back. - ScopedThread([&] { - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. POSIX threads, however, require that all - // threads have the same UIDs, so using the setuid wrapper sets all threads' - // real UID. - EXPECT_THAT(syscall(SYS_setuid, -1), SyscallFailsWithErrno(EINVAL)); - - const uid_t uid = absl::GetFlag(FLAGS_scratch_uid1); - EXPECT_THAT(syscall(SYS_setuid, uid), SyscallSucceeds()); - // "If the effective UID of the caller is root (more precisely: if the - // caller has the CAP_SETUID capability), the real UID and saved set-user-ID - // are also set." - setuid(2) - EXPECT_NO_ERRNO(CheckUIDs(uid, uid, uid)); - }); -} - -TEST(UidGidRootTest, Setgid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - EXPECT_THAT(syscall(SYS_setgid, -1), SyscallFailsWithErrno(EINVAL)); - - ScopedThread([&] { - const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1); - EXPECT_THAT(syscall(SYS_setgid, gid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); - }); -} - -TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) { -#pragma push_macro("allow_setgid") -#undef setgid - - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - int old_gid = getgid(); - auto clean = Cleanup([old_gid] { setgid(old_gid); }); - - const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1); - // NOTE(b/64676707): Do setgid in a separate thread so that we can test if - // info.si_pid is set correctly. - ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); }); - EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); - -#pragma pop_macro("allow_setgid") -} - -TEST(UidGidRootTest, Setreuid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // "Supplying a value of -1 for either the real or effective user ID forces - // the system to leave that ID unchanged." - setreuid(2) - EXPECT_THAT(syscall(SYS_setreuid, -1, -1), SyscallSucceeds()); - - EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. After - // calling setuid(non-zero-UID), there is no way to get root privileges - // back. - ScopedThread([&] { - const uid_t ruid = absl::GetFlag(FLAGS_scratch_uid1); - const uid_t euid = absl::GetFlag(FLAGS_scratch_uid2); - - EXPECT_THAT(syscall(SYS_setreuid, ruid, euid), SyscallSucceeds()); - - // "If the real user ID is set or the effective user ID is set to a value - // not equal to the previous real user ID, the saved set-user-ID will be - // set to the new effective user ID." - setreuid(2) - EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, euid)); - }); -} - -TEST(UidGidRootTest, Setregid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - EXPECT_THAT(syscall(SYS_setregid, -1, -1), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); - - ScopedThread([&] { - const gid_t rgid = absl::GetFlag(FLAGS_scratch_gid1); - const gid_t egid = absl::GetFlag(FLAGS_scratch_gid2); - ASSERT_THAT(syscall(SYS_setregid, rgid, egid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, egid)); - }); -} - -TEST(UidGidRootTest, Setresuid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // "If one of the arguments equals -1, the corresponding value is not - // changed." - setresuid(2) - EXPECT_THAT(syscall(SYS_setresuid, -1, -1, -1), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. After - // calling setuid(non-zero-UID), there is no way to get root privileges - // back. - ScopedThread([&] { - const uid_t ruid = 12345; - const uid_t euid = 23456; - const uid_t suid = 34567; - - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. posix threads, however, require that - // all threads have the same UIDs, so using the setuid wrapper sets all - // threads' real UID. - EXPECT_THAT(syscall(SYS_setresuid, ruid, euid, suid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, suid)); - }); -} - -TEST(UidGidRootTest, Setresgid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - EXPECT_THAT(syscall(SYS_setresgid, -1, -1, -1), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); - - ScopedThread([&] { - const gid_t rgid = 12345; - const gid_t egid = 23456; - const gid_t sgid = 34567; - ASSERT_THAT(syscall(SYS_setresgid, rgid, egid, sgid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, sgid)); - }); -} - -TEST(UidGidRootTest, Setgroups) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - std::vector<gid_t> list = {123, 500}; - ASSERT_THAT(setgroups(list.size(), list.data()), SyscallSucceeds()); - std::vector<gid_t> list2(list.size()); - ASSERT_THAT(getgroups(list2.size(), list2.data()), SyscallSucceeds()); - EXPECT_THAT(list, UnorderedElementsAreArray(list2)); - - // "EFAULT: list has an invalid address." - EXPECT_THAT(getgroups(100, reinterpret_cast<gid_t*>(-1)), - SyscallFailsWithErrno(EFAULT)); -} - -TEST(UidGidRootTest, Setuid_prlimit) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // Do seteuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. - ScopedThread([&] { - // Use syscall instead of glibc setuid wrapper because we want this - // seteuid call to only apply to this task. POSIX threads, however, - // require that all threads have the same UIDs, so using the seteuid - // wrapper sets all threads' UID. - EXPECT_THAT(syscall(SYS_setreuid, -1, 65534), SyscallSucceeds()); - - // Despite the UID change, we should be able to get our own limits. - struct rlimit rl = {}; - EXPECT_THAT(prlimit(0, RLIMIT_NOFILE, NULL, &rl), SyscallSucceeds()); - }); -} - -} // namespace - -} // namespace testing -} // namespace gvisor |