| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/platform.h" |
| |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/xattr.h> |
| |
| #include <linux/fs.h> |
| |
| #include <fcntl.h> |
| #include <string> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <gtest/gtest.h> |
| |
| using base::FilePath; |
| |
| namespace cryptohome { |
| |
| class PlatformTest : public ::testing::Test { |
| public: |
| virtual ~PlatformTest() {} |
| protected: |
| std::string GetRandomSuffix() { |
| return platform_.GetRandomSuffix(); |
| } |
| FilePath GetTempName() { |
| FilePath temp_directory; |
| EXPECT_TRUE(base::GetTempDir(&temp_directory)); |
| return temp_directory.Append(GetRandomSuffix()); |
| } |
| |
| Platform platform_; |
| }; |
| |
| TEST_F(PlatformTest, WriteFileCanBeReadBack) { |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| EXPECT_TRUE(platform_.WriteStringToFile(filename, content)); |
| std::string output; |
| EXPECT_TRUE(platform_.ReadFileToString(filename, &output)); |
| EXPECT_EQ(content, output); |
| platform_.DeleteFile(filename, false /* recursive */); |
| } |
| |
| TEST_F(PlatformTest, WriteFileSets0666) { |
| const mode_t mask = 0000; |
| const mode_t mode = 0666; |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| const mode_t old_mask = platform_.SetMask(mask); |
| EXPECT_TRUE(platform_.WriteStringToFile(filename, content)); |
| mode_t file_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(filename, &file_mode)); |
| EXPECT_EQ(mode & ~mask, file_mode & 0777); |
| platform_.SetMask(old_mask); |
| platform_.DeleteFile(filename, false /* recursive */); |
| } |
| |
| TEST_F(PlatformTest, WriteFileCreatesMissingParentDirectoriesWith0700) { |
| const mode_t mask = 0000; |
| const mode_t mode = 0700; |
| const FilePath dirname(GetTempName()); |
| const FilePath subdirname(dirname.Append(GetRandomSuffix())); |
| const FilePath filename(subdirname.Append(GetRandomSuffix())); |
| const std::string content("blablabla"); |
| EXPECT_TRUE(platform_.WriteStringToFile(filename, content)); |
| mode_t dir_mode = 0; |
| mode_t subdir_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(dirname, &dir_mode)); |
| EXPECT_TRUE(platform_.GetPermissions(subdirname, &subdir_mode)); |
| EXPECT_EQ(mode & ~mask, dir_mode & 0777); |
| EXPECT_EQ(mode & ~mask, subdir_mode & 0777); |
| const mode_t old_mask = platform_.SetMask(mask); |
| platform_.DeleteFile(dirname, true /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, WriteStringToFileAtomicCanBeReadBack) { |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| EXPECT_TRUE(platform_.WriteStringToFileAtomic(filename, content, 0644)); |
| std::string output; |
| EXPECT_TRUE(platform_.ReadFileToString(filename, &output)); |
| EXPECT_EQ(content, output); |
| platform_.DeleteFile(filename, false /* recursive */); |
| } |
| |
| TEST_F(PlatformTest, WriteStringToFileAtomicHonorsMode) { |
| const mode_t mask = 0000; |
| const mode_t mode = 0616; |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| const mode_t old_mask = platform_.SetMask(mask); |
| EXPECT_TRUE(platform_.WriteStringToFileAtomic(filename, content, mode)); |
| mode_t file_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(filename, &file_mode)); |
| EXPECT_EQ(mode & ~mask, file_mode & 0777); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, WriteStringToFileAtomicHonorsUmask) { |
| const mode_t mask = 0073; |
| const mode_t mode = 0777; |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| const mode_t old_mask = platform_.SetMask(mask); |
| EXPECT_TRUE(platform_.WriteStringToFileAtomic(filename, content, mode)); |
| mode_t file_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(filename, &file_mode)); |
| EXPECT_EQ(mode & ~mask, file_mode & 0777); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, |
| WriteStringToFileAtomicCreatesMissingParentDirectoriesWith0700) { |
| const mode_t mask = 0000; |
| const mode_t mode = 0700; |
| const FilePath dirname(GetTempName()); |
| const FilePath subdirname(dirname.Append(GetRandomSuffix())); |
| const FilePath filename(subdirname.Append(GetRandomSuffix())); |
| const std::string content("blablabla"); |
| EXPECT_TRUE(platform_.WriteStringToFileAtomic(filename, content, 0777)); |
| mode_t dir_mode = 0; |
| mode_t subdir_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(dirname, &dir_mode)); |
| EXPECT_TRUE(platform_.GetPermissions(subdirname, &subdir_mode)); |
| EXPECT_EQ(mode & ~mask, dir_mode & 0777); |
| EXPECT_EQ(mode & ~mask, subdir_mode & 0777); |
| const mode_t old_mask = platform_.SetMask(mask); |
| platform_.DeleteFile(dirname, true /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, WriteStringToFileAtomicDurableCanBeReadBack) { |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| EXPECT_TRUE( |
| platform_.WriteStringToFileAtomicDurable(filename, content, 0644)); |
| std::string output; |
| EXPECT_TRUE(platform_.ReadFileToString(filename, &output)); |
| EXPECT_EQ(content, output); |
| platform_.DeleteFile(filename, false /* recursive */); |
| } |
| |
| TEST_F(PlatformTest, WriteStringToFileAtomicDurableHonorsMode) { |
| const mode_t mask = 0000; |
| const mode_t mode = 0616; |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| const mode_t old_mask = platform_.SetMask(mask); |
| EXPECT_TRUE( |
| platform_.WriteStringToFileAtomicDurable(filename, content, mode)); |
| mode_t file_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(filename, &file_mode)); |
| EXPECT_EQ(mode & ~mask, file_mode & 0777); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, WriteStringToFileAtomicDurableHonorsUmask) { |
| const mode_t mask = 0073; |
| const mode_t mode = 0777; |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| const mode_t old_mask = platform_.SetMask(mask); |
| EXPECT_TRUE( |
| platform_.WriteStringToFileAtomicDurable(filename, content, mode)); |
| mode_t file_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(filename, &file_mode)); |
| EXPECT_EQ(mode & ~mask, file_mode & 0777); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, |
| WriteStringToFileAtomicDurableCreatesMissingParentDirectoriesWith0700) { |
| const mode_t mask = 0000; |
| const mode_t mode = 0700; |
| const FilePath dirname(GetTempName()); |
| const FilePath subdirname(dirname.Append(GetRandomSuffix())); |
| const FilePath filename(subdirname.Append(GetRandomSuffix())); |
| const std::string content("blablabla"); |
| EXPECT_TRUE(platform_.WriteStringToFileAtomicDurable( |
| filename, content, 0777)); |
| mode_t dir_mode = 0; |
| mode_t subdir_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(dirname, &dir_mode)); |
| EXPECT_TRUE(platform_.GetPermissions(subdirname, &subdir_mode)); |
| EXPECT_EQ(mode & ~mask, dir_mode & 0777); |
| EXPECT_EQ(mode & ~mask, subdir_mode & 0777); |
| const mode_t old_mask = platform_.SetMask(mask); |
| platform_.DeleteFile(dirname, true /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, TouchFileDurable) { |
| const FilePath filename(GetTempName()); |
| EXPECT_TRUE(platform_.TouchFileDurable(filename)); |
| int64_t size = -1; |
| EXPECT_TRUE(platform_.GetFileSize(filename, &size)); |
| EXPECT_EQ(0, size); |
| platform_.DeleteFile(filename, false /* recursive */); |
| } |
| |
| TEST_F(PlatformTest, TouchFileDurableSets0666) { |
| const mode_t mask = 0000; |
| const mode_t mode = 0666; |
| const FilePath filename(GetTempName()); |
| const mode_t old_mask = platform_.SetMask(mask); |
| EXPECT_TRUE(platform_.TouchFileDurable(filename)); |
| mode_t file_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(filename, &file_mode)); |
| EXPECT_EQ(mode & ~mask, file_mode & 0777); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, TouchFileDurableHonorsUmask) { |
| const mode_t mask = 0066; |
| const mode_t mode = 0640; |
| const FilePath filename(GetTempName()); |
| const mode_t old_mask = platform_.SetMask(mask); |
| EXPECT_TRUE(platform_.TouchFileDurable(filename)); |
| mode_t file_mode = 0; |
| EXPECT_TRUE(platform_.GetPermissions(filename, &file_mode)); |
| EXPECT_EQ(mode & ~mask, file_mode & 0777); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.SetMask(old_mask); |
| } |
| |
| TEST_F(PlatformTest, DataSyncFileHasSaneReturnCodes) { |
| const FilePath filename(GetTempName()); |
| const FilePath dirname(GetTempName()); |
| platform_.CreateDirectory(dirname); |
| EXPECT_FALSE(platform_.DataSyncFile(dirname)); |
| EXPECT_FALSE(platform_.DataSyncFile(filename)); |
| EXPECT_TRUE(platform_.WriteStringToFile(filename, "bla")); |
| EXPECT_TRUE(platform_.DataSyncFile(filename)); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.DeleteFile(dirname, true /* recursive */); |
| } |
| |
| TEST_F(PlatformTest, SyncDirectoryHasSaneReturnCodes) { |
| const FilePath filename(GetTempName()); |
| const FilePath dirname(GetTempName()); |
| platform_.WriteStringToFile(filename, "bla"); |
| EXPECT_FALSE(platform_.SyncDirectory(filename)); |
| EXPECT_FALSE(platform_.SyncDirectory(dirname)); |
| EXPECT_TRUE(platform_.CreateDirectory(dirname)); |
| EXPECT_TRUE(platform_.SyncDirectory(dirname)); |
| platform_.DeleteFile(filename, false /* recursive */); |
| platform_.DeleteFile(dirname, true /* recursive */); |
| } |
| |
| TEST_F(PlatformTest, HasExtendedFileAttribute) { |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| ASSERT_TRUE(platform_.WriteStringToFile(filename, content)); |
| const std::string name("user.foo"); |
| const std::string value("bar"); |
| |
| ASSERT_EQ(0, setxattr(filename.value().c_str(), name.c_str(), value.c_str(), |
| value.length(), 0)); |
| |
| EXPECT_TRUE(platform_.HasExtendedFileAttribute(filename, name)); |
| |
| EXPECT_FALSE(platform_.HasExtendedFileAttribute( |
| FilePath("file_not_exist"), name)); |
| EXPECT_FALSE(platform_.HasExtendedFileAttribute( |
| filename, "user.name_not_exist")); |
| } |
| |
| TEST_F(PlatformTest, HasNoDumpFileAttribute) { |
| const FilePath filename(GetTempName()); |
| const std::string content("blablabla"); |
| ASSERT_TRUE(platform_.WriteStringToFile(filename, content)); |
| |
| EXPECT_FALSE(platform_.HasNoDumpFileAttribute(filename)); |
| |
| int fd; |
| ASSERT_GT(fd = open(filename.value().c_str(), O_RDONLY), 0); |
| int flags = FS_UNRM_FL | FS_NODUMP_FL; |
| ASSERT_GE(ioctl(fd, FS_IOC_SETFLAGS, &flags), 0); |
| |
| EXPECT_TRUE(platform_.HasNoDumpFileAttribute(filename)); |
| close(fd); |
| } |
| |
| TEST_F(PlatformTest, DecodeProcInfoLineGood) { |
| std::string mount_info_contents; |
| |
| mount_info_contents.append("73 24 179:1 /beg/uid1/mount/user "); |
| mount_info_contents.append("/home/user/uid1 rw,nodev,relatime - ext4 "); |
| mount_info_contents.append("/dev/mmcblk0p1 rw,commit=600,data=ordered"); |
| |
| std::vector<std::string> args; |
| size_t fs_idx; |
| |
| EXPECT_TRUE(platform_.DecodeProcInfoLine( |
| mount_info_contents, &args, &fs_idx)); |
| EXPECT_EQ(fs_idx, 7); |
| } |
| |
| TEST_F(PlatformTest, DecodeProcInfoLineCorruptedMountInfo) { |
| std::string mount_info_contents; |
| |
| mount_info_contents.append("73 24 179:1 /beg/uid1/mount/user "); |
| mount_info_contents.append("/home/user/uid1 rw,nodev,relatime hypen ext4 "); |
| mount_info_contents.append("/dev/mmcblk0p1 rw,commit=600,data=ordered"); |
| |
| std::vector<std::string> args; |
| size_t fs_idx; |
| |
| EXPECT_FALSE(platform_.DecodeProcInfoLine( |
| mount_info_contents, &args, &fs_idx)); |
| } |
| |
| TEST_F(PlatformTest, DecodeProcInfoLineIncompleteMountInfo) { |
| std::string mount_info_contents; |
| |
| mount_info_contents.append("73 24 179:1 /beg/uid1/mount/user "); |
| |
| std::vector<std::string> args; |
| size_t fs_idx; |
| |
| EXPECT_FALSE(platform_.DecodeProcInfoLine( |
| mount_info_contents, &args, &fs_idx)); |
| } |
| |
| TEST_F(PlatformTest, GetMountsBySourcePrefixExt4) { |
| base::FilePath mount_info; |
| FILE *fp; |
| std::string filesystem, device_in, device_out, mount_info_contents; |
| |
| mount_info_contents.append("73 24 179:1 /beg/uid1/mount/user "); |
| mount_info_contents.append("/home/user/uid1 rw,nodev,relatime - ext4 "); |
| mount_info_contents.append("/dev/mmcblk0p1 rw,commit=600,data=ordered"); |
| |
| fp = base::CreateAndOpenTemporaryFile(&mount_info); |
| ASSERT_TRUE(fp != NULL); |
| EXPECT_EQ(fwrite(mount_info_contents.c_str(), |
| mount_info_contents.length(), 1, fp), 1); |
| EXPECT_EQ(fclose(fp), 0); |
| |
| platform_.set_mount_info_path(mount_info); |
| |
| /* Fails if item is missing. */ |
| std::multimap<const FilePath, const FilePath> mounts; |
| EXPECT_FALSE(platform_.GetMountsBySourcePrefix(FilePath("monkey"), &mounts)); |
| |
| /* Works normally. */ |
| mounts.clear(); |
| EXPECT_TRUE(platform_.GetMountsBySourcePrefix(FilePath("/beg"), &mounts)); |
| EXPECT_EQ(mounts.size(), 1); |
| auto it = mounts.begin(); |
| EXPECT_EQ(it->first.value(), "/beg/uid1/mount/user"); |
| EXPECT_EQ(it->second.value(), "/home/user/uid1"); |
| |
| /* Clean up. */ |
| EXPECT_TRUE(base::DeleteFile(mount_info, false)); |
| } |
| |
| TEST_F(PlatformTest, GetMountsBySourcePrefixECryptFs) { |
| base::FilePath mount_info; |
| FILE *fp; |
| std::string filesystem, device_in, device_out, mount_info_contents; |
| |
| mount_info_contents.append("84 24 0:29 /user /home/user/uid2 "); |
| mount_info_contents.append("rw,nosuid,nodev,noexec,relatime - ecryptfs "); |
| mount_info_contents.append("/beg/uid2/vault rw,ecryp..."); |
| |
| fp = base::CreateAndOpenTemporaryFile(&mount_info); |
| ASSERT_TRUE(fp != NULL); |
| EXPECT_EQ(fwrite(mount_info_contents.c_str(), |
| mount_info_contents.length(), 1, fp), 1); |
| EXPECT_EQ(fclose(fp), 0); |
| |
| platform_.set_mount_info_path(mount_info); |
| |
| /* Fails if item is missing. */ |
| std::multimap<const FilePath, const FilePath> mounts; |
| EXPECT_FALSE(platform_.GetMountsBySourcePrefix(FilePath("monkey"), &mounts)); |
| |
| /* Works normally. */ |
| mounts.clear(); |
| EXPECT_TRUE(platform_.GetMountsBySourcePrefix(FilePath("/beg"), &mounts)); |
| EXPECT_EQ(mounts.size(), 1); |
| auto it = mounts.begin(); |
| EXPECT_EQ(it->first.value(), "/beg/uid2/vault"); |
| EXPECT_EQ(it->second.value(), "/home/user/uid2"); |
| |
| /* Clean up. */ |
| EXPECT_TRUE(base::DeleteFile(mount_info, false)); |
| } |
| |
| } // namespace cryptohome |