1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/chrome_cleaner/ipc/sandbox.h"
6 
7 #include <windows.h>
8 
9 #include <algorithm>
10 #include <string>
11 
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/command_line.h"
15 #include "base/files/file_util.h"
16 #include "base/logging.h"
17 #include "base/macros.h"
18 #include "base/numerics/safe_conversions.h"
19 #include "base/optional.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/test/multiprocess_test.h"
24 #include "base/win/scoped_handle.h"
25 #include "chrome/chrome_cleaner/buildflags.h"
26 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
27 #include "chrome/chrome_cleaner/logging/scoped_logging.h"
28 #include "chrome/chrome_cleaner/os/disk_util.h"
29 #include "chrome/chrome_cleaner/os/initializer.h"
30 #include "sandbox/win/src/sandbox_factory.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "testing/multiprocess_func_list.h"
34 
35 namespace chrome_cleaner {
36 
37 namespace {
38 
39 constexpr int kChildExitCode = 420042;
40 
41 class MockSandboxTargetServices : public sandbox::TargetServices {
42  public:
43   MockSandboxTargetServices() = default;
44   ~MockSandboxTargetServices() = default;
45 
46   MOCK_METHOD0(Init, sandbox::ResultCode());
47   MOCK_METHOD0(LowerToken, void());
48   MOCK_METHOD0(GetState, sandbox::ProcessState*());
49 };
50 
51 class TestSandboxSetupHooks : public SandboxSetupHooks {
52  public:
TestSandboxSetupHooks(base::Process * process_holder)53   explicit TestSandboxSetupHooks(base::Process* process_holder)
54       : process_holder_(process_holder) {}
55   ~TestSandboxSetupHooks() override = default;
56 
TargetSpawned(const base::Process & target_process,const base::win::ScopedHandle & target_thread)57   ResultCode TargetSpawned(
58       const base::Process& target_process,
59       const base::win::ScopedHandle& target_thread) override {
60     DCHECK(process_holder_);
61     *process_holder_ = target_process.Duplicate();
62     return SandboxSetupHooks::TargetSpawned(target_process, target_thread);
63   }
64 
65  private:
66   base::Process* process_holder_;
67 
68   DISALLOW_COPY_AND_ASSIGN(TestSandboxSetupHooks);
69 };
70 
71 class TestSandboxTargetHooks : public SandboxTargetHooks {
72  public:
73   TestSandboxTargetHooks() = default;
74   ~TestSandboxTargetHooks() override = default;
75 
TargetDroppedPrivileges(const base::CommandLine & command_line)76   ResultCode TargetDroppedPrivileges(
77       const base::CommandLine& command_line) override {
78     return RESULT_CODE_SUCCESS;
79   }
80 
81  private:
82   DISALLOW_COPY_AND_ASSIGN(TestSandboxTargetHooks);
83 };
84 
85 class SandboxTest : public base::MultiProcessTest {
86  protected:
87   SandboxTest() = default;
88 
SetUp()89   void SetUp() override {
90     // Delete the sandbox process log file. If we decide to log its content,
91     // it will only contain output relevant to this test case. DeleteFile
92     // returns true on success or if attempting to delete a file that does not
93     // exist.
94     sandbox_process_log_file_path_ =
95         ScopedLogging::GetLogFilePath(kSandboxLogFileSuffix);
96     EXPECT_TRUE(base::DeleteFile(sandbox_process_log_file_path_));
97   }
98 
TearDown()99   void TearDown() override {
100     if (HasFailure() && base::PathExists(sandbox_process_log_file_path_)) {
101       // Collect the sandbox process log file, and dump the contents, to help
102       // debugging failures.
103       std::string log_file_contents;
104       if (base::ReadFileToString(sandbox_process_log_file_path_,
105                                  &log_file_contents)) {
106         std::vector<base::StringPiece> lines = base::SplitStringPiece(
107             log_file_contents, "\n", base::TRIM_WHITESPACE,
108             base::SPLIT_WANT_NONEMPTY);
109         LOG(ERROR) << "Dumping sandbox process log";
110         for (const auto& line : lines) {
111           LOG(ERROR) << "Sandbox process log line: " << line;
112         }
113       } else {
114         LOG(ERROR) << "Failed to read sandbox process log file";
115       }
116     }
117   }
118 
119   // Starts a child process using the StartSandboxTarget API.
SpawnMockSandboxProcess(base::Process * process)120   bool SpawnMockSandboxProcess(base::Process* process) {
121     TestSandboxSetupHooks setup_hooks(process);
122     return chrome_cleaner::StartSandboxTarget(
123                MakeCmdLine("MockSandboxProcessMain"), &setup_hooks,
124                SandboxType::kTest) == RESULT_CODE_SUCCESS;
125   }
126 
TestRunSandboxTarget(const base::CommandLine & command_line)127   ResultCode TestRunSandboxTarget(const base::CommandLine& command_line) {
128     TestSandboxTargetHooks hooks;
129     return RunSandboxTarget(command_line, &mock_sandbox_target_services_,
130                             &hooks);
131   }
132 
133  private:
134   MockSandboxTargetServices mock_sandbox_target_services_;
135   base::FilePath sandbox_process_log_file_path_;
136 };
137 
MULTIPROCESS_TEST_MAIN(MockSandboxProcessMain)138 MULTIPROCESS_TEST_MAIN(MockSandboxProcessMain) {
139   base::FilePath product_path;
140   bool success = chrome_cleaner::GetAppDataProductDirectory(&product_path);
141   CHECK(success);
142   auto* target_services = sandbox::SandboxFactory::GetTargetServices();
143   CHECK(target_services);
144   NotifyInitializationDone();
145   target_services->LowerToken();
146 
147   bool have_write_access = false;
148   base::FilePath temp_file;
149   if (base::CreateTemporaryFileInDir(product_path, &temp_file)) {
150     have_write_access = true;
151     base::DeleteFile(temp_file);
152   }
153 
154 #if BUILDFLAG(IS_OFFICIAL_CHROME_CLEANER_BUILD)
155   CHECK(!have_write_access);
156 #else
157   CHECK(have_write_access);
158 #endif
159 
160   // Lower token, test access.
161   return kChildExitCode;
162 }
163 
164 }  // namespace
165 
TEST_F(SandboxTest,SpawnSandboxTarget)166 TEST_F(SandboxTest, SpawnSandboxTarget) {
167   base::Process target_process;
168   EXPECT_TRUE(SpawnMockSandboxProcess(&target_process));
169   EXPECT_TRUE(target_process.IsValid());
170 
171   int exit_code = -1;
172   EXPECT_TRUE(target_process.WaitForExitWithTimeout(
173       base::TimeDelta::FromSeconds(10), &exit_code));
174   EXPECT_EQ(kChildExitCode, exit_code);
175 }
176 
TEST_F(SandboxTest,RunSandboxTarget)177 TEST_F(SandboxTest, RunSandboxTarget) {
178   EXPECT_EQ(RESULT_CODE_SUCCESS,
179             TestRunSandboxTarget(*base::CommandLine::ForCurrentProcess()));
180 }
181 
182 }  // namespace chrome_cleaner
183