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