| // Copyright 2021 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "dlp/fanotify_watcher.h" | |
| #include <memory> | |
| #include <sys/fanotify.h> | |
| #include <utility> | |
| #include "base/files/file.h" | |
| #include "base/files/file_path.h" | |
| #include "base/files/file_util.h" | |
| #include "base/files/scoped_temp_dir.h" | |
| #include "base/functional/callback.h" | |
| #include "base/test/task_environment.h" | |
| #include "gtest/gtest.h" | |
| namespace dlp { | |
| namespace { | |
| constexpr int kInode = 1; | |
| constexpr time_t kCrtime = 2; | |
| constexpr int kPid = 3; | |
| } // namespace | |
| class FanotifyWatcherTest : public ::testing::Test, | |
| public FanotifyWatcher::Delegate { | |
| public: | |
| FanotifyWatcherTest() = default; | |
| FanotifyWatcherTest(const FanotifyWatcherTest&) = delete; | |
| FanotifyWatcherTest& operator=(const FanotifyWatcherTest&) = delete; | |
| ~FanotifyWatcherTest() override = default; | |
| void SetUp() override { | |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
| ASSERT_TRUE(base::CreatePipe(&perm_fd_out_, &perm_fd_in_)); | |
| watcher_ = std::make_unique<FanotifyWatcher>(this, perm_fd_in_.release(), | |
| /*fanotify_notif_fd=*/-1); | |
| } | |
| void ProcessFileOpenRequest( | |
| FileId id, int pid, base::OnceCallback<void(bool)> callback) override { | |
| EXPECT_EQ(id.first, kInode); | |
| EXPECT_EQ(id.second, kCrtime); | |
| EXPECT_EQ(pid, kPid); | |
| counter_++; | |
| std::move(callback).Run(file_open_allowed_); | |
| } | |
| void OnFileDeleted(ino64_t inode) override { | |
| // Not expected to be called. | |
| FAIL(); | |
| }; | |
| void OnFanotifyError(FanotifyError error) override { | |
| // Not expected to be called. | |
| FAIL(); | |
| }; | |
| bool SimulateFileOpen(int expected_counter) { | |
| base::FilePath temp_file; | |
| base::ScopedFD file_fd = base::CreateAndOpenFdForTemporaryFileInDir( | |
| temp_dir_.GetPath(), &temp_file); | |
| const int fd = file_fd.get(); | |
| watcher_->OnFileOpenRequested( | |
| kInode, kCrtime, kPid, std::move(file_fd), | |
| std::make_unique<FanotifyReaderThread::FanotifyReplyWatchdog>()); | |
| EXPECT_EQ(counter_, expected_counter); | |
| struct fanotify_response response = {}; | |
| HANDLE_EINTR(read(perm_fd_out_.get(), &response, sizeof(response))); | |
| EXPECT_EQ(response.fd, fd); | |
| return response.response == FAN_ALLOW; | |
| } | |
| protected: | |
| base::test::TaskEnvironment task_environment_; | |
| bool file_open_allowed_ = true; | |
| std::unique_ptr<FanotifyWatcher> watcher_; | |
| private: | |
| uint32_t counter_ = 0; | |
| base::ScopedTempDir temp_dir_; | |
| base::ScopedFD perm_fd_in_, perm_fd_out_; | |
| }; | |
| TEST_F(FanotifyWatcherTest, FileOpen) { | |
| int requests_counter = 0; | |
| file_open_allowed_ = true; | |
| // Allowed when not active. | |
| EXPECT_TRUE(SimulateFileOpen(requests_counter)); | |
| // Allowed when processed. | |
| watcher_->SetActive(true); | |
| requests_counter++; | |
| EXPECT_TRUE(SimulateFileOpen(requests_counter)); | |
| // Allowed when disactivated. | |
| watcher_->SetActive(false); | |
| EXPECT_TRUE(SimulateFileOpen(requests_counter)); | |
| // Not allowed when processed. | |
| file_open_allowed_ = false; | |
| watcher_->SetActive(true); | |
| requests_counter++; | |
| EXPECT_FALSE(SimulateFileOpen(requests_counter)); | |
| // Allowed when disactivated again. | |
| watcher_->SetActive(false); | |
| EXPECT_TRUE(SimulateFileOpen(requests_counter)); | |
| } | |
| } // namespace dlp |