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