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