| Index: base/file_util_unittest.cc
|
| diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc
|
| index c44b804f00949c85b4fd877c261e85f45a9155a6..4eb0e0aa029a7e7ebbfcd0c3c98cc5db2ea2317b 100644
|
| --- a/base/file_util_unittest.cc
|
| +++ b/base/file_util_unittest.cc
|
| @@ -110,6 +110,27 @@ bool DeleteReparsePoint(HANDLE source) {
|
| }
|
| #endif
|
|
|
| +#if defined(OS_POSIX)
|
| +// Provide a simple way to change the permissions bits on |path| in tests.
|
| +// ASSERT failures will return, but not stop the test. Caller should wrap
|
| +// calls to this function in ASSERT_NO_FATAL_FAILURE().
|
| +void ChangePosixFilePermissions(const FilePath& path,
|
| + mode_t mode_bits_to_set,
|
| + mode_t mode_bits_to_clear) {
|
| + ASSERT_EQ(0u, mode_bits_to_set & mode_bits_to_clear)
|
| + << "Can't set and clear the same bit.";
|
| +
|
| + struct stat stat_buf;
|
| + ASSERT_EQ(0, stat(path.value().c_str(), &stat_buf));
|
| +
|
| + mode_t new_mode_bits = stat_buf.st_mode;
|
| + new_mode_bits |= mode_bits_to_set;
|
| + new_mode_bits &= ~mode_bits_to_clear;
|
| +
|
| + ASSERT_EQ(0, chmod(path.value().c_str(), new_mode_bits));
|
| +}
|
| +#endif // defined(OS_POSIX)
|
| +
|
| const wchar_t bogus_content[] = L"I'm cannon fodder.";
|
|
|
| const file_util::FileEnumerator::FileType FILES_AND_DIRECTORIES =
|
| @@ -1813,4 +1834,225 @@ TEST_F(FileUtilTest, IsDirectoryEmpty) {
|
| EXPECT_FALSE(file_util::IsDirectoryEmpty(empty_dir));
|
| }
|
|
|
| +#if defined(OS_POSIX)
|
| +
|
| +// Testing IsPathControlledByAdmin() is hard, because there is no
|
| +// way a test can make a file owned by root, or change file paths
|
| +// at the root of the file system. IsPathControlledByAdmin()
|
| +// is implemented as a call to IsPathControlledByUser, which gives
|
| +// us the ability to test with paths under the test's temp directory,
|
| +// using a user id we control.
|
| +// Pull tests of IsPathControlledByUserTest() into a separate test class
|
| +// with a common SetUp() method.
|
| +class IsPathControlledByUserTest : public FileUtilTest {
|
| + protected:
|
| + virtual void SetUp() {
|
| + FileUtilTest::SetUp();
|
| +
|
| + // Create a basic structure used by each test.
|
| + // base_dir_
|
| + // |-> sub_dir_
|
| + // |-> text_file_
|
| +
|
| + base_dir_ = temp_dir_.path().AppendASCII("base_dir");
|
| + ASSERT_TRUE(file_util::CreateDirectory(base_dir_));
|
| +
|
| + sub_dir_ = base_dir_.AppendASCII("sub_dir");
|
| + ASSERT_TRUE(file_util::CreateDirectory(sub_dir_));
|
| +
|
| + text_file_ = sub_dir_.AppendASCII("file.txt");
|
| + CreateTextFile(text_file_, L"This text file has some text in it.");
|
| +
|
| + // Our user and group id.
|
| + uid_ = getuid();
|
| + gid_ = getgid();
|
| + }
|
| +
|
| + FilePath base_dir_;
|
| + FilePath sub_dir_;
|
| + FilePath text_file_;
|
| + uid_t uid_;
|
| + gid_t gid_;
|
| +};
|
| +
|
| +TEST_F(IsPathControlledByUserTest, BadPaths) {
|
| + // File does not exist.
|
| + FilePath does_not_exist = base_dir_.AppendASCII("does")
|
| + .AppendASCII("not")
|
| + .AppendASCII("exist");
|
| +
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, does_not_exist, uid_, gid_));
|
| +
|
| + // |base| not a subpath of |path|.
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(sub_dir_, base_dir_, uid_, gid_));
|
| +
|
| + // An empty base path will fail to be a prefix for any path.
|
| + FilePath empty;
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(empty, base_dir_, uid_, gid_));
|
| +
|
| + // Finding that a bad call fails proves nothing unless a good call succeeds.
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| +}
|
| +
|
| +TEST_F(IsPathControlledByUserTest, Symlinks) {
|
| + // Symlinks in the path should cause failure.
|
| +
|
| + // Symlink to the file at the end of the path.
|
| + FilePath file_link = base_dir_.AppendASCII("file_link");
|
| + ASSERT_TRUE(file_util::CreateSymbolicLink(text_file_, file_link))
|
| + << "Failed to create symlink.";
|
| +
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, file_link, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(file_link, file_link, uid_, gid_));
|
| +
|
| + // Symlink from one directory to another within the path.
|
| + FilePath link_to_sub_dir_ = base_dir_.AppendASCII("link_to_sub_dir_");
|
| + ASSERT_TRUE(file_util::CreateSymbolicLink(sub_dir_, link_to_sub_dir_))
|
| + << "Failed to create symlink.";
|
| +
|
| + FilePath file_path_with_link = link_to_sub_dir_.AppendASCII("file.txt");
|
| + ASSERT_TRUE(file_util::PathExists(file_path_with_link));
|
| +
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(
|
| + base_dir_, file_path_with_link, uid_, gid_));
|
| +
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(
|
| + link_to_sub_dir_, file_path_with_link, uid_, gid_));
|
| +
|
| + // Symlinks in parents of base path are allowed.
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(
|
| + file_path_with_link, file_path_with_link, uid_, gid_));
|
| +}
|
| +
|
| +TEST_F(IsPathControlledByUserTest, OwnershipChecks) {
|
| + // Get a uid that is not the uid of files we create.
|
| + uid_t bad_uid = uid_ + 1;
|
| +
|
| + // Get a gid that is not ours.
|
| + gid_t bad_gid = gid_ + 1;
|
| +
|
| + // Make all files and directories non-world-writable.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
|
| +
|
| + // We control these paths.
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +
|
| + // Another user does not control these paths.
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, bad_uid, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, bad_uid, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, bad_uid, gid_));
|
| +
|
| + // Another group does not control the paths.
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, bad_gid));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, bad_gid));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, bad_gid));
|
| +}
|
| +
|
| +TEST_F(IsPathControlledByUserTest, WriteBitChecks) {
|
| +
|
| + // Make all files and directories non-world-writable.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
|
| +
|
| + // Initialy, we control all parts of teh path.
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +
|
| + // Make base_dir_ world-writable.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(base_dir_, S_IWOTH, 0u));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +
|
| + // Make sub_dir_ world writable.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(sub_dir_, S_IWOTH, 0u));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +
|
| + // Make text_file_ world writable.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(text_file_, S_IWOTH, 0u));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +
|
| + // Make sub_dir_ non-world writable.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +
|
| + // Make base_dir_ non-world-writable.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_FALSE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +
|
| + // Back to the initial state: Nothing is writable, so every path
|
| + // should pass.
|
| + ASSERT_NO_FATAL_FAILURE(
|
| + ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, sub_dir_, uid_, gid_));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(base_dir_, text_file_, uid_, gid_));
|
| + EXPECT_TRUE(
|
| + file_util::IsPathControlledByUser(sub_dir_, text_file_, uid_, gid_));
|
| +}
|
| +
|
| +#endif // defined(OS_POSIX)
|
| +
|
| } // namespace
|
|
|