| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Unit tests for initgroups. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "hdr/errno_macros.h" | |
| #include "hdr/signal_macros.h" | |
| #include "hdr/types/gid_t.h" | |
| #include "hdr/types/size_t.h" | |
| #include "hdr/types/struct_group.h" | |
| #include "src/grp/endgrent.h" | |
| #include "src/grp/getgrent.h" | |
| #include "src/grp/grp_utils.h" | |
| #include "src/grp/initgroups.h" | |
| #include "src/grp/setgrent.h" | |
| #include "src/unistd/getuid.h" | |
| #include "test/UnitTest/ErrnoSetterMatcher.h" | |
| #include "test/UnitTest/Test.h" | |
| #include "test/src/grp/grp_test_utils.h" | |
| using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; | |
| using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; | |
| TEST_F(LlvmLibcGrpTest, InitgroupsPrivilegeCheck) { | |
| const char *content = "root:x:0:root\n" | |
| "users:x:100:user1,user2\n" | |
| "admins:x:300:user1\n"; | |
| ScopedGroupFile test_file(libc_make_test_file_path("initgroups_priv.test"), | |
| content); | |
| if (LIBC_NAMESPACE::getuid() == 0) { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("user1", 1000), Succeeds(0)); | |
| } else { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("user1", 1000), Fails(EPERM)); | |
| } | |
| } | |
| TEST_F(LlvmLibcGrpTest, InitgroupsNonexistentUser) { | |
| const char *content = "wheel:x:10:root\n"; | |
| ScopedGroupFile test_file( | |
| libc_make_test_file_path("initgroups_nonexist.test"), content); | |
| if (LIBC_NAMESPACE::getuid() == 0) { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("nonexistent", 1000), Succeeds(0)); | |
| } else { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("nonexistent", 1000), Fails(EPERM)); | |
| } | |
| } | |
| TEST_F(LlvmLibcGrpTest, InitgroupsNonexistentFile) { | |
| LIBC_NAMESPACE::grp::TESTONLY_set_group_path( | |
| libc_make_test_file_path("initgroups_missing.test")); | |
| if (LIBC_NAMESPACE::getuid() == 0) { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("anyuser", 1000), Succeeds(0)); | |
| } else { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("anyuser", 1000), Fails(EPERM)); | |
| } | |
| } | |
| TEST_F(LlvmLibcGrpTest, InitgroupsDoesNotDisturbIteration) { | |
| const char *content = "group1:x:1:user1\n" | |
| "group2:x:2:user2\n" | |
| "group3:x:3:user1\n"; | |
| ScopedGroupFile test_file(libc_make_test_file_path("initgroups_iter.test"), | |
| content); | |
| LIBC_NAMESPACE::setgrent(); | |
| const auto first = LIBC_NAMESPACE::grp::read_next(); | |
| ASSERT_TRUE(first.has_value()); | |
| ASSERT_NE(first.value(), nullptr); | |
| EXPECT_STREQ(first.value()->gr_name, "group1"); | |
| // initgroups reads the database using a scoped stream. | |
| if (LIBC_NAMESPACE::getuid() == 0) { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("user2", 50), Succeeds(0)); | |
| } else { | |
| EXPECT_THAT(LIBC_NAMESPACE::initgroups("user2", 50), Fails(EPERM)); | |
| } | |
| const auto second = LIBC_NAMESPACE::grp::read_next(); | |
| ASSERT_TRUE(second.has_value()); | |
| ASSERT_NE(second.value(), nullptr); | |
| EXPECT_STREQ(second.value()->gr_name, "group2"); | |
| LIBC_NAMESPACE::endgrent(); | |
| } | |
| #if defined(LIBC_ADD_NULL_CHECKS) | |
| TEST_F(LlvmLibcGrpTest, NullUserCrash) { | |
| ASSERT_DEATH([] { LIBC_NAMESPACE::initgroups(nullptr, 1000); }, | |
| WITH_SIGNAL(-1)); | |
| } | |
| #endif // LIBC_ADD_NULL_CHECKS |