github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/verity_ioctl.cc (about) 1 // Copyright 2021 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 <stdint.h> 16 #include <stdlib.h> 17 #include <sys/mount.h> 18 #include <sys/stat.h> 19 #include <time.h> 20 21 #include <iomanip> 22 #include <sstream> 23 24 #include "gmock/gmock.h" 25 #include "gtest/gtest.h" 26 #include "test/util/capability_util.h" 27 #include "test/util/fs_util.h" 28 #include "test/util/mount_util.h" 29 #include "test/util/temp_path.h" 30 #include "test/util/test_util.h" 31 #include "test/util/verity_util.h" 32 33 namespace gvisor { 34 namespace testing { 35 36 namespace { 37 38 class IoctlTest : public ::testing::Test { 39 protected: 40 void SetUp() override { 41 // Verity is implemented in VFS2. 42 SKIP_IF(IsRunningWithVFS1()); 43 44 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); 45 // Mount a tmpfs file system, to be wrapped by a verity fs. 46 tmpfs_dir_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 47 ASSERT_THAT(mount("", tmpfs_dir_.path().c_str(), "tmpfs", 0, ""), 48 SyscallSucceeds()); 49 50 // Create a new file in the tmpfs mount. 51 file_ = ASSERT_NO_ERRNO_AND_VALUE( 52 TempPath::CreateFileWith(tmpfs_dir_.path(), kContents, 0777)); 53 filename_ = Basename(file_.path()); 54 } 55 56 TempPath tmpfs_dir_; 57 TempPath file_; 58 std::string filename_; 59 }; 60 61 TEST_F(IoctlTest, Enable) { 62 // Mount a verity fs on the existing tmpfs mount. 63 std::string mount_opts = "lower_path=" + tmpfs_dir_.path(); 64 auto const verity_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 65 ASSERT_THAT( 66 mount("", verity_dir.path().c_str(), "verity", 0, mount_opts.c_str()), 67 SyscallSucceeds()); 68 69 // Confirm that the verity flag is absent. 70 int flag = 0; 71 auto const fd = ASSERT_NO_ERRNO_AND_VALUE( 72 Open(JoinPath(verity_dir.path(), filename_), O_RDONLY, 0777)); 73 ASSERT_THAT(ioctl(fd.get(), FS_IOC_GETFLAGS, &flag), SyscallSucceeds()); 74 EXPECT_EQ(flag & FS_VERITY_FL, 0); 75 76 // Enable the file and confirm that the verity flag is present. 77 ASSERT_THAT(ioctl(fd.get(), FS_IOC_ENABLE_VERITY), SyscallSucceeds()); 78 ASSERT_THAT(ioctl(fd.get(), FS_IOC_GETFLAGS, &flag), SyscallSucceeds()); 79 EXPECT_EQ(flag & FS_VERITY_FL, FS_VERITY_FL); 80 } 81 82 TEST_F(IoctlTest, Measure) { 83 // Mount a verity fs on the existing tmpfs mount. 84 std::string mount_opts = "lower_path=" + tmpfs_dir_.path(); 85 auto const verity_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 86 ASSERT_THAT( 87 mount("", verity_dir.path().c_str(), "verity", 0, mount_opts.c_str()), 88 SyscallSucceeds()); 89 90 // Confirm that the file cannot be measured. 91 auto const fd = ASSERT_NO_ERRNO_AND_VALUE( 92 Open(JoinPath(verity_dir.path(), filename_), O_RDONLY, 0777)); 93 uint8_t digest_array[sizeof(struct fsverity_digest) + kMaxDigestSize] = {0}; 94 struct fsverity_digest* digest = 95 reinterpret_cast<struct fsverity_digest*>(digest_array); 96 digest->digest_size = kMaxDigestSize; 97 ASSERT_THAT(ioctl(fd.get(), FS_IOC_MEASURE_VERITY, digest), 98 SyscallFailsWithErrno(ENODATA)); 99 100 // Enable the file and confirm that the file can be measured. 101 ASSERT_THAT(ioctl(fd.get(), FS_IOC_ENABLE_VERITY), SyscallSucceeds()); 102 ASSERT_THAT(ioctl(fd.get(), FS_IOC_MEASURE_VERITY, digest), 103 SyscallSucceeds()); 104 EXPECT_EQ(digest->digest_size, kDefaultDigestSize); 105 } 106 107 TEST_F(IoctlTest, Mount) { 108 std::string verity_dir = 109 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 110 111 // Make sure the file can be open and read in the mounted verity fs. 112 auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE( 113 Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777)); 114 char buf[sizeof(kContents)]; 115 EXPECT_THAT(ReadFd(verity_fd.get(), buf, sizeof(kContents)), 116 SyscallSucceeds()); 117 } 118 119 TEST_F(IoctlTest, NonExistingFile) { 120 std::string verity_dir = 121 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 122 123 // Confirm that opening a non-existing file in the verity-enabled directory 124 // triggers the expected error instead of verification failure. 125 EXPECT_THAT( 126 open(JoinPath(verity_dir, filename_ + "abc").c_str(), O_RDONLY, 0777), 127 SyscallFailsWithErrno(ENOENT)); 128 } 129 130 TEST_F(IoctlTest, ModifiedFile) { 131 std::string verity_dir = 132 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 133 134 // Modify the file and check verification failure upon reading from it. 135 auto const fd = ASSERT_NO_ERRNO_AND_VALUE( 136 Open(JoinPath(tmpfs_dir_.path(), filename_), O_RDWR, 0777)); 137 ASSERT_NO_ERRNO(FlipRandomBit(fd.get(), sizeof(kContents) - 1)); 138 139 auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE( 140 Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777)); 141 char buf[sizeof(kContents)]; 142 EXPECT_THAT(pread(verity_fd.get(), buf, 16, 0), SyscallFailsWithErrno(EIO)); 143 } 144 145 TEST_F(IoctlTest, ModifiedMerkle) { 146 std::string verity_dir = 147 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 148 149 // Modify the Merkle file and check verification failure upon opening the 150 // corresponding file. 151 auto const fd = ASSERT_NO_ERRNO_AND_VALUE( 152 Open(MerklePath(JoinPath(tmpfs_dir_.path(), filename_)), O_RDWR, 0777)); 153 auto stat = ASSERT_NO_ERRNO_AND_VALUE(Fstat(fd.get())); 154 ASSERT_NO_ERRNO(FlipRandomBit(fd.get(), stat.st_size)); 155 156 EXPECT_THAT(open(JoinPath(verity_dir, filename_).c_str(), O_RDONLY, 0777), 157 SyscallFailsWithErrno(EIO)); 158 } 159 160 TEST_F(IoctlTest, ModifiedDirMerkle) { 161 std::string verity_dir = 162 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 163 164 // Modify the Merkle file for the parent directory and check verification 165 // failure upon opening the corresponding file. 166 auto const fd = ASSERT_NO_ERRNO_AND_VALUE( 167 Open(MerkleRootPath(JoinPath(tmpfs_dir_.path(), "root")), O_RDWR, 0777)); 168 auto stat = ASSERT_NO_ERRNO_AND_VALUE(Fstat(fd.get())); 169 ASSERT_NO_ERRNO(FlipRandomBit(fd.get(), stat.st_size)); 170 171 EXPECT_THAT(open(JoinPath(verity_dir, filename_).c_str(), O_RDONLY, 0777), 172 SyscallFailsWithErrno(EIO)); 173 } 174 175 TEST_F(IoctlTest, Stat) { 176 std::string verity_dir = 177 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 178 179 struct stat st; 180 EXPECT_THAT(stat(JoinPath(verity_dir, filename_).c_str(), &st), 181 SyscallSucceeds()); 182 } 183 184 TEST_F(IoctlTest, ModifiedStat) { 185 std::string verity_dir = 186 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 187 188 EXPECT_THAT(chmod(JoinPath(tmpfs_dir_.path(), filename_).c_str(), 0644), 189 SyscallSucceeds()); 190 struct stat st; 191 EXPECT_THAT(stat(JoinPath(verity_dir, filename_).c_str(), &st), 192 SyscallFailsWithErrno(EIO)); 193 } 194 195 TEST_F(IoctlTest, DeleteFile) { 196 std::string verity_dir = 197 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 198 199 EXPECT_THAT(unlink(JoinPath(tmpfs_dir_.path(), filename_).c_str()), 200 SyscallSucceeds()); 201 EXPECT_THAT(open(JoinPath(verity_dir, filename_).c_str(), O_RDONLY, 0777), 202 SyscallFailsWithErrno(EIO)); 203 } 204 205 TEST_F(IoctlTest, DeleteMerkle) { 206 std::string verity_dir = 207 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 208 209 EXPECT_THAT( 210 unlink(MerklePath(JoinPath(tmpfs_dir_.path(), filename_)).c_str()), 211 SyscallSucceeds()); 212 EXPECT_THAT(open(JoinPath(verity_dir, filename_).c_str(), O_RDONLY, 0777), 213 SyscallFailsWithErrno(EIO)); 214 } 215 216 TEST_F(IoctlTest, RenameFile) { 217 std::string verity_dir = 218 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 219 220 std::string new_file_name = "renamed-" + filename_; 221 EXPECT_THAT(rename(JoinPath(tmpfs_dir_.path(), filename_).c_str(), 222 JoinPath(tmpfs_dir_.path(), new_file_name).c_str()), 223 SyscallSucceeds()); 224 EXPECT_THAT(open(JoinPath(verity_dir, filename_).c_str(), O_RDONLY, 0777), 225 SyscallFailsWithErrno(EIO)); 226 } 227 228 TEST_F(IoctlTest, RenameMerkle) { 229 std::string verity_dir = 230 ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); 231 232 std::string new_file_name = "renamed-" + filename_; 233 EXPECT_THAT( 234 rename(MerklePath(JoinPath(tmpfs_dir_.path(), filename_)).c_str(), 235 MerklePath(JoinPath(tmpfs_dir_.path(), new_file_name)).c_str()), 236 SyscallSucceeds()); 237 EXPECT_THAT(open(JoinPath(verity_dir, filename_).c_str(), O_RDONLY, 0777), 238 SyscallFailsWithErrno(EIO)); 239 } 240 241 } // namespace 242 243 } // namespace testing 244 } // namespace gvisor