blob: cee1bca1a88d7e63336976fd3c90925c7f94d108 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/chrome_cleaner/ipc/sandbox.h"
#include <windows.h>
#include <algorithm>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/test/multiprocess_test.h"
#include "base/win/scoped_handle.h"
#include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
#include "chrome/chrome_cleaner/logging/scoped_logging.h"
#include "chrome/chrome_cleaner/os/disk_util.h"
#include "chrome/chrome_cleaner/os/initializer.h"
#include "sandbox/win/src/sandbox_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace chrome_cleaner {
namespace {
constexpr int kChildExitCode = 420042;
class MockSandboxTargetServices : public sandbox::TargetServices {
public:
MockSandboxTargetServices() = default;
~MockSandboxTargetServices() = default;
MOCK_METHOD0(Init, sandbox::ResultCode());
MOCK_METHOD0(LowerToken, void());
MOCK_METHOD0(GetState, sandbox::ProcessState*());
};
class TestSandboxSetupHooks : public SandboxSetupHooks {
public:
explicit TestSandboxSetupHooks(base::Process* process_holder)
: process_holder_(process_holder) {}
~TestSandboxSetupHooks() override = default;
ResultCode TargetSpawned(
const base::Process& target_process,
const base::win::ScopedHandle& target_thread) override {
DCHECK(process_holder_);
*process_holder_ = target_process.Duplicate();
return SandboxSetupHooks::TargetSpawned(target_process, target_thread);
}
private:
base::Process* process_holder_;
DISALLOW_COPY_AND_ASSIGN(TestSandboxSetupHooks);
};
class TestSandboxTargetHooks : public SandboxTargetHooks {
public:
TestSandboxTargetHooks() = default;
~TestSandboxTargetHooks() override = default;
ResultCode TargetDroppedPrivileges(
const base::CommandLine& command_line) override {
return RESULT_CODE_SUCCESS;
}
private:
DISALLOW_COPY_AND_ASSIGN(TestSandboxTargetHooks);
};
class SandboxTest : public base::MultiProcessTest {
protected:
SandboxTest() = default;
void SetUp() override {
// Delete the sandbox process log file. If we decide to log its content,
// it will only contain output relevant to this test case. DeleteFile
// returns true on success or if attempting to delete a file that does not
// exist.
sandbox_process_log_file_path_ =
ScopedLogging::GetLogFilePath(kSandboxLogFileSuffix);
EXPECT_TRUE(base::DeleteFile(sandbox_process_log_file_path_, false));
}
void TearDown() override {
if (HasFailure() && base::PathExists(sandbox_process_log_file_path_)) {
// Collect the sandbox process log file, and dump the contents, to help
// debugging failures.
std::string log_file_contents;
if (base::ReadFileToString(sandbox_process_log_file_path_,
&log_file_contents)) {
std::vector<base::StringPiece> lines = base::SplitStringPiece(
log_file_contents, "\n", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
LOG(ERROR) << "Dumping sandbox process log";
for (const auto& line : lines) {
LOG(ERROR) << "Sandbox process log line: " << line;
}
} else {
LOG(ERROR) << "Failed to read sandbox process log file";
}
}
}
// Starts a child process using the StartSandboxTarget API.
bool SpawnMockSandboxProcess(base::Process* process) {
TestSandboxSetupHooks setup_hooks(process);
return chrome_cleaner::StartSandboxTarget(
MakeCmdLine("MockSandboxProcessMain"), &setup_hooks,
SandboxType::kTest) == RESULT_CODE_SUCCESS;
}
ResultCode TestRunSandboxTarget(const base::CommandLine& command_line) {
TestSandboxTargetHooks hooks;
return RunSandboxTarget(command_line, &mock_sandbox_target_services_,
&hooks);
}
private:
MockSandboxTargetServices mock_sandbox_target_services_;
base::FilePath sandbox_process_log_file_path_;
};
MULTIPROCESS_TEST_MAIN(MockSandboxProcessMain) {
base::FilePath product_path;
bool success = chrome_cleaner::GetAppDataProductDirectory(&product_path);
CHECK(success);
auto* target_services = sandbox::SandboxFactory::GetTargetServices();
CHECK(target_services);
NotifyInitializationDone();
target_services->LowerToken();
bool have_write_access = false;
base::FilePath temp_file;
if (base::CreateTemporaryFileInDir(product_path, &temp_file)) {
have_write_access = true;
base::DeleteFile(temp_file, /*recursive=*/false);
}
#if defined(CHROME_CLEANER_OFFICIAL_BUILD)
CHECK(!have_write_access);
#else
CHECK(have_write_access);
#endif
// Lower token, test access.
return kChildExitCode;
}
} // namespace
TEST_F(SandboxTest, SpawnSandboxTarget) {
base::Process target_process;
EXPECT_TRUE(SpawnMockSandboxProcess(&target_process));
EXPECT_TRUE(target_process.IsValid());
int exit_code = -1;
EXPECT_TRUE(target_process.WaitForExitWithTimeout(
base::TimeDelta::FromSeconds(10), &exit_code));
EXPECT_EQ(kChildExitCode, exit_code);
}
TEST_F(SandboxTest, RunSandboxTarget) {
EXPECT_EQ(RESULT_CODE_SUCCESS,
TestRunSandboxTarget(*base::CommandLine::ForCurrentProcess()));
}
} // namespace chrome_cleaner