Commit c18d5933 authored by S. Ganesh's avatar S. Ganesh Committed by Commit Bot

Implement RunRecoveryCRXElevated on the Elevator COM interface.

Bug: 833687
Change-Id: I60ca6e5ef75a8dabbf9e15d5a10e2738fa83d303
Reviewed-on: https://chromium-review.googlesource.com/c/1295732
Commit-Queue: S. Ganesh <ganesh@chromium.org>
Reviewed-by: default avatarSorin Jianu <sorin@chromium.org>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarScott Graham <scottmg@chromium.org>
Reviewed-by: default avatarAdam Langley <agl@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Reviewed-by: default avatarFinnur Thorarinsson <finnur@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605239}
parent 518057ec
......@@ -376,9 +376,8 @@ bool DeleteFileAfterReboot(const FilePath& path) {
if (path.value().length() >= MAX_PATH)
return false;
return MoveFileEx(path.value().c_str(), NULL,
MOVEFILE_DELAY_UNTIL_REBOOT |
MOVEFILE_REPLACE_EXISTING) != FALSE;
return ::MoveFileEx(path.value().c_str(), nullptr,
MOVEFILE_DELAY_UNTIL_REBOOT);
}
bool ReplaceFile(const FilePath& from_path,
......
......@@ -87,6 +87,10 @@ struct BASE_EXPORT LaunchOptions {
#if defined(OS_WIN)
bool start_hidden = false;
// Sets STARTF_FORCEOFFFEEDBACK so that the feedback cursor is forced off
// while the process is starting.
bool feedback_cursor_off = false;
// Windows can inherit handles when it launches child processes.
// See https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873
// for a good overview of Windows handle inheritance.
......
......@@ -239,9 +239,11 @@ Process LaunchProcess(const string16& cmdline,
flags |= EXTENDED_STARTUPINFO_PRESENT;
}
if (options.feedback_cursor_off)
startup_info->dwFlags |= STARTF_FORCEOFFFEEDBACK;
if (options.empty_desktop_name)
startup_info->lpDesktop = const_cast<wchar_t*>(L"");
startup_info->dwFlags = STARTF_USESHOWWINDOW;
startup_info->dwFlags |= STARTF_USESHOWWINDOW;
startup_info->wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOWNORMAL;
if (options.stdin_handle || options.stdout_handle || options.stderr_handle) {
......
......@@ -49,19 +49,22 @@ executable("elevation_service") {
"//base",
"//build/win:default_exe_manifest",
"//chrome/install_static:primary_module",
"//components/crx_file",
"//third_party/zlib/google:zip",
]
}
source_set("lib") {
visibility = [ ":*" ]
public = [
"elevated_recovery_impl.h",
"elevator.h",
"service_main.h",
]
sources = [
"elevated_recovery_impl.cc",
"elevator.cc",
"service_main.cc",
]
......@@ -87,8 +90,20 @@ process_version_rc_template("version_resources") {
output = "$target_gen_dir/elevation_service_exe.rc"
}
copy("elevation_service_unittests_files") {
sources = [
"//components/test/data/crx_file/valid_no_publisher.crx3",
"//components/test/data/crx_file/valid_publisher.crx3",
"//components/test/data/update_client/ChromeRecovery.crx3",
]
outputs = [
"$root_out_dir/elevated_recovery_unittest/{{source_file_part}}",
]
}
test("elevation_service_unittests") {
sources = [
"elevated_recovery_unittest.cc",
"run_all_unittests.cc",
"service_main_unittest.cc",
]
......@@ -102,10 +117,13 @@ test("elevation_service_unittests") {
"//base/test:test_support",
"//chrome/install_static:install_static_util",
"//chrome/install_static/test:test_support",
"//components/crx_file",
"//testing/gtest",
"//third_party/zlib/google:zip",
]
data_deps = [
":elevation_service",
":elevation_service_unittests_files",
]
}
include_rules = [
"+chrome/install_static",
"+components/crx_file",
"+third_party/zlib",
]
// 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/elevation_service/elevated_recovery_impl.h"
#include <objbase.h>
#include <string>
#include <utility>
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/strings/utf_string_conversions.h"
#include "base/version.h"
#include "base/win/scoped_process_information.h"
#include "chrome/install_static/install_util.h"
#include "third_party/zlib/google/zip.h"
namespace elevation_service {
namespace {
// The hard-coded Recovery subdirectory where the CRX is unpacked and executed.
constexpr base::FilePath::CharType kRecoveryDirectory[] =
FILE_PATH_LITERAL("ChromeRecovery");
// The hard-coded Recovery executable. This file comes from the CRX, and we
// create an elevated process from it.
constexpr base::FilePath::CharType kRecoveryExeName[] =
FILE_PATH_LITERAL("ChromeRecovery.exe");
// The hard-coded SHA256 of the SubjectPublicKeyInfo used to sign the Recovery
// CRX which contains ChromeRecovery.exe.
std::vector<uint8_t> GetRecoveryCRXHash() {
return std::vector<uint8_t>{0x5f, 0x94, 0xe0, 0x3c, 0x64, 0x30, 0x9f, 0xbc,
0xfe, 0x00, 0x9a, 0x27, 0x3e, 0x52, 0xbf, 0xa5,
0x84, 0xb9, 0xb3, 0x75, 0x07, 0x29, 0xde, 0xfa,
0x32, 0x76, 0xd9, 0x93, 0xb5, 0xa3, 0xce, 0x02};
}
// This function is only meant to be called when a Windows API function errors
// out, and the corresponding ::GetLastResult is expected to be set to an error.
// There could be cases where ::GetLastError() is not set correctly, this
// function returns E_FAIL in those cases.
HRESULT HRESULTFromLastError() {
const auto error_code = ::GetLastError();
return (error_code != ERROR_SUCCESS) ? HRESULT_FROM_WIN32(error_code)
: E_FAIL;
}
// Opens and returns the COM caller's |process| given the process id, or the
// current process if |proc_id| is 0.
HRESULT OpenCallingProcess(uint32_t proc_id, base::Process* process) {
DCHECK(process);
if (!proc_id) {
*process = base::Process::Current();
return S_OK;
}
HRESULT hr = ::CoImpersonateClient();
if (FAILED(hr))
return hr;
base::ScopedClosureRunner revert_to_self(
base::BindOnce(base::IgnoreResult(&::CoRevertToSelf)));
*process = base::Process::OpenWithAccess(proc_id, PROCESS_DUP_HANDLE);
return process->IsValid() ? S_OK : HRESULTFromLastError();
}
// Validates the provided CRX using the |crx_hash|, and if validation succeeds,
// unpacks the CRX under |unpack_under_path|. Returns the unpacked CRX
// directory in |unpacked_crx_dir|.
HRESULT ValidateAndUnpackCRX(const base::FilePath& from_crx_path,
const crx_file::VerifierFormat& crx_format,
const std::vector<uint8_t>& crx_hash,
const base::FilePath& unpack_under_path,
base::ScopedTempDir* unpacked_crx_dir) {
DCHECK(unpacked_crx_dir);
base::ScopedTempDir to_dir;
if (!to_dir.CreateUniqueTempDirUnderPath(unpack_under_path))
return HRESULTFromLastError();
const base::FilePath to_crx_path =
to_dir.GetPath().Append(from_crx_path.BaseName());
if (!base::CopyFile(from_crx_path, to_crx_path))
return HRESULTFromLastError();
std::string public_key;
if (crx_file::Verify(to_crx_path, crx_format, {crx_hash}, {}, &public_key,
nullptr) != crx_file::VerifierResult::OK_FULL) {
return CRYPT_E_NO_MATCH;
}
if (!zip::Unzip(to_crx_path, to_dir.GetPath()))
return E_UNEXPECTED;
LOG_IF(WARNING, !base::DeleteFile(to_crx_path, false));
LOG_IF(WARNING, !unpacked_crx_dir->Set(to_dir.Take()));
return S_OK;
}
// Runs the executable |path_and_name| with the provided |args|. The returned
// |proc_handle| is a process handle that is valid for the |calling_process|
// process.
HRESULT LaunchCmd(const base::CommandLine& command_line,
const base::Process& calling_process,
base::win::ScopedHandle* proc_handle) {
DCHECK(!command_line.GetCommandLineString().empty());
DCHECK(proc_handle);
base::LaunchOptions options = {};
options.feedback_cursor_off = true;
base::GetTempDir(&options.current_directory);
base::Process proc = base::LaunchProcess(command_line, options);
if (!proc.IsValid())
return HRESULTFromLastError();
HANDLE duplicate_proc_handle = nullptr;
constexpr DWORD desired_access =
PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE;
bool res = ::DuplicateHandle(
::GetCurrentProcess(), // Current process.
proc.Handle(), // Process handle to duplicate.
calling_process.Handle(), // Process receiving the handle.
&duplicate_proc_handle, // Duplicated handle.
desired_access, // Access requested for the new handle.
FALSE, // Don't inherit the new handle.
0) != 0;
if (!res)
return HRESULTFromLastError();
proc_handle->Set(duplicate_proc_handle);
return S_OK;
}
HRESULT ValidateCRXArgs(const base::string16& browser_appid,
const base::string16& browser_version,
const base::string16& session_id) {
if (!browser_appid.empty()) {
GUID guid = {};
HRESULT hr = ::IIDFromString(browser_appid.c_str(), &guid);
if (FAILED(hr))
return hr;
}
const base::Version version(base::UTF16ToASCII(browser_version));
if (!version.IsValid())
return E_INVALIDARG;
GUID session_guid = {};
return ::IIDFromString(session_id.c_str(), &session_guid);
}
// Deletes all the files and subdirectories within |directory_path|. Errors are
// ignored.
void DeleteDirectoryFiles(const base::FilePath& directory_path) {
base::FileEnumerator file_enum(
directory_path, false,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
for (base::FilePath current = file_enum.Next(); !current.empty();
current = file_enum.Next()) {
base::DeleteFile(current, true);
}
}
// Schedules deletion after reboot of |dir_name| as well as all the files and
// subdirectories within |dir_name|. Errors are ignored.
void ScheduleDirectoryForDeletion(const base::FilePath& dir_name) {
// First schedule all the files within |dir_name| for deletion.
base::FileEnumerator file_enum(dir_name, false, base::FileEnumerator::FILES);
for (base::FilePath file = file_enum.Next(); !file.empty();
file = file_enum.Next()) {
base::DeleteFileAfterReboot(file);
}
// Then recurse to all the subdirectories.
base::FileEnumerator dir_enum(dir_name, false,
base::FileEnumerator::DIRECTORIES);
for (base::FilePath sub_dir = dir_enum.Next(); !sub_dir.empty();
sub_dir = dir_enum.Next()) {
ScheduleDirectoryForDeletion(sub_dir);
}
// Now schedule the empty directory itself.
base::DeleteFileAfterReboot(dir_name);
}
// Returns the ChromeRecovery directory under Google\Chrome. For machine
// installs, this directory is under %ProgramFiles%, which is writeable only by
// adminstrators. We use this secure directory to validate and unpack the CRX to
// prevent tampering.
HRESULT GetChromeRecoveryDirectory(base::FilePath* dir) {
base::FilePath recovery_dir;
if (!base::PathService::Get(base::DIR_EXE, &recovery_dir))
return HRESULTFromLastError();
recovery_dir = recovery_dir.DirName().DirName().Append(kRecoveryDirectory);
*dir = std::move(recovery_dir);
return S_OK;
}
} // namespace
HRESULT CleanupChromeRecoveryDirectory() {
base::FilePath recovery_dir;
HRESULT hr = GetChromeRecoveryDirectory(&recovery_dir);
if (FAILED(hr))
return hr;
DeleteDirectoryFiles(recovery_dir);
return S_OK;
}
HRESULT RunChromeRecoveryCRX(const base::FilePath& crx_path,
const base::string16& browser_appid,
const base::string16& browser_version,
const base::string16& session_id,
uint32_t caller_proc_id,
base::win::ScopedHandle* proc_handle) {
if (crx_path.empty() || !proc_handle)
return E_INVALIDARG;
HRESULT hr = ValidateCRXArgs(browser_appid, browser_version, session_id);
if (FAILED(hr))
return hr;
base::CommandLine args(base::CommandLine::NO_PROGRAM);
if (!browser_appid.empty())
args.AppendSwitchNative("appguid", browser_appid);
args.AppendSwitchNative("browser-version", browser_version);
args.AppendSwitchNative("sessionid", session_id);
args.AppendSwitch("system");
base::FilePath unpack_dir;
hr = GetChromeRecoveryDirectory(&unpack_dir);
if (FAILED(hr))
return hr;
return RunCRX(crx_path, args,
crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF,
GetRecoveryCRXHash(), unpack_dir,
base::FilePath(kRecoveryExeName), caller_proc_id, proc_handle);
}
HRESULT RunCRX(const base::FilePath& crx_path,
const base::CommandLine& args,
const crx_file::VerifierFormat& crx_format,
const std::vector<uint8_t>& crx_hash,
const base::FilePath& unpack_under_path,
const base::FilePath& exe_filename,
uint32_t caller_proc_id,
base::win::ScopedHandle* proc_handle) {
DCHECK(proc_handle);
DCHECK(!crx_path.empty());
DCHECK(!crx_hash.empty());
DCHECK(!unpack_under_path.empty());
DCHECK(!exe_filename.empty());
base::Process calling_process;
HRESULT hr = OpenCallingProcess(caller_proc_id, &calling_process);
if (FAILED(hr))
return hr;
base::ScopedTempDir unpacked_crx_dir;
hr = ValidateAndUnpackCRX(crx_path, crx_format, crx_hash, unpack_under_path,
&unpacked_crx_dir);
if (FAILED(hr))
return hr;
const base::FilePath path_and_name =
unpacked_crx_dir.GetPath().Append(exe_filename);
base::CommandLine command_line(path_and_name);
command_line.AppendArguments(args, false);
base::win::ScopedHandle scoped_proc_handle;
hr = LaunchCmd(command_line, calling_process, &scoped_proc_handle);
if (FAILED(hr))
return hr;
ScheduleDirectoryForDeletion(unpacked_crx_dir.Take());
*proc_handle = std::move(scoped_proc_handle);
return hr;
}
} // namespace elevation_service
// 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.
#ifndef CHROME_ELEVATION_SERVICE_ELEVATED_RECOVERY_IMPL_H_
#define CHROME_ELEVATION_SERVICE_ELEVATED_RECOVERY_IMPL_H_
#include <stdint.h>
#include <vector>
#include "base/strings/string16.h"
#include "base/win/scoped_handle.h"
#include "components/crx_file/crx_verifier.h"
namespace base {
class CommandLine;
class FilePath;
} // namespace base
namespace elevation_service {
// Delete stale files within the Chrome Recovery directory left over from any
// previous calls to RunChromeRecoveryCRX.
HRESULT CleanupChromeRecoveryDirectory();
// Verifies the CRX and then runs ChromeRecovery.exe embedded within the
// provided |crx_path|. The returned |proc_handle| is a process handle that is
// valid for the |caller_proc_id| process, or the current process if
// |caller_proc_id| is 0. Please read the doc comment in
// elevation_service_idl.idl for the other parameters.
HRESULT RunChromeRecoveryCRX(const base::FilePath& crx_path,
const base::string16& browser_appid,
const base::string16& browser_version,
const base::string16& session_id,
uint32_t caller_proc_id,
base::win::ScopedHandle* proc_handle);
// Verifies the CRX and then runs |exe_filename| embedded within the provided
// |crx_path|. The returned |proc_handle| is a process handle that is valid for
// the |caller_proc_id| process, or the current process if |caller_proc_id| is
// 0. |unpacked_under_path| is expected to be eventually deleted by the caller.
HRESULT RunCRX(const base::FilePath& crx_path,
const base::CommandLine& args,
const crx_file::VerifierFormat& crx_format,
const std::vector<uint8_t>& crx_hash,
const base::FilePath& unpack_under_path,
const base::FilePath& exe_filename,
uint32_t caller_proc_id,
base::win::ScopedHandle* proc_handle);
} // namespace elevation_service
#endif // CHROME_ELEVATION_SERVICE_ELEVATED_RECOVERY_IMPL_H_
// 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/elevation_service/elevated_recovery_impl.h"
#include <windows.h>
#include <memory>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace elevation_service {
namespace {
// There is no exe in "valid_publisher.crx3", so we point to the manifest
// instead for these tests.
constexpr base::FilePath::CharType kManifestJSONFileName[] =
FILE_PATH_LITERAL("manifest.json");
// The SHA256 of the SubjectPublicKeyInfo used to sign "valid_publisher.crx3",
// which houses extension id ojjgnpkioondelmggbekfhllhdaimnho.
std::vector<uint8_t> GetValidPublisherCrx3Hash() {
return std::vector<uint8_t>{0xe9, 0x96, 0xdf, 0xa8, 0xee, 0xd3, 0x4b, 0xc6,
0x61, 0x4a, 0x57, 0xbb, 0x73, 0x08, 0xcd, 0x7e,
0x51, 0x9b, 0xcc, 0x69, 0x08, 0x41, 0xe1, 0x96,
0x9f, 0x7c, 0xb1, 0x73, 0xef, 0x16, 0x80, 0x0a};
}
// The test exe within components/test/data/update_client/ChromeRecovery.crx3.
constexpr base::FilePath::CharType kRecoveryExeName[] =
FILE_PATH_LITERAL("ChromeRecovery.exe");
// The SHA256 of the SubjectPublicKeyInfo used to sign
// components/test/data/update_client/ChromeRecovery.crx3.
std::vector<uint8_t> GetRunactionTestWinCrx3Hash() {
return std::vector<uint8_t>{0x69, 0xfc, 0x41, 0xf6, 0x17, 0x20, 0xc6, 0x36,
0x92, 0xcd, 0x95, 0x76, 0x69, 0xf6, 0x28, 0xcc,
0xbe, 0x98, 0x4b, 0x93, 0x17, 0xd6, 0x9c, 0xb3,
0x64, 0x0c, 0x0d, 0x25, 0x61, 0xc5, 0x80, 0x1d};
}
const base::FilePath GetUnpackDir() {
base::FilePath path;
base::PathService::Get(base::DIR_TEMP, &path);
return path;
}
const base::FilePath TestFile(const std::string& file) {
base::FilePath path;
base::PathService::Get(base::DIR_MODULE, &path);
return path.AppendASCII("elevated_recovery_unittest")
.AppendASCII(file);
}
} // namespace
TEST(ElevatedRecoveryTest, Do_RunChromeRecoveryCRX_InvalidArgs) {
base::win::ScopedHandle proc_handle;
// Empty browser_appid/browser_version/session_id.
EXPECT_EQ(E_INVALIDARG,
elevation_service::RunChromeRecoveryCRX(
TestFile("valid_publisher.crx3"), base::string16(),
base::string16(), base::string16(), 0, &proc_handle));
// Invalid browser_appid, valid browser_version/session_id.
EXPECT_EQ(E_INVALIDARG,
elevation_service::RunChromeRecoveryCRX(
TestFile("valid_publisher.crx3"), L"invalidappid",
L"1.2.3.4", L"{c49ab053-2387-4809-b188-1902648802e1}", 0,
&proc_handle));
// Empty browser_appid, invalid browser_version, valid session_id.
EXPECT_EQ(E_INVALIDARG,
elevation_service::RunChromeRecoveryCRX(
TestFile("valid_publisher.crx3"), base::string16(),
L"invalidbrowserversion",
L"{c49ab053-2387-4809-b188-1902648802e1}", 0, &proc_handle));
// Valid browser_appid, invalid browser_version, valid session_id.
EXPECT_EQ(
E_INVALIDARG,
elevation_service::RunChromeRecoveryCRX(
TestFile("valid_publisher.crx3"),
L"{c49ab053-2387-4809-b188-1902648802e1}", L"invalidbrowserversion",
L"{c49ab053-2387-4809-b188-1902648802e1}", 0, &proc_handle));
// Valid browser_appid, valid browser_version, invalid session_id.
EXPECT_EQ(E_INVALIDARG,
elevation_service::RunChromeRecoveryCRX(
TestFile("valid_publisher.crx3"),
L"{c49ab053-2387-4809-b188-1902648802e1}", L"57.8.0.1",
L"invalidsessionid", 0, &proc_handle));
}
TEST(ElevatedRecoveryTest, Do_RunCRX_InvalidArgs) {
base::win::ScopedHandle proc_handle;
// Non-matching CRX/CRX-hash.
EXPECT_EQ(CRYPT_E_NO_MATCH,
elevation_service::RunCRX(
TestFile("valid_no_publisher.crx3"),
base::CommandLine(base::CommandLine::NO_PROGRAM),
crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF,
GetValidPublisherCrx3Hash(), GetUnpackDir(),
base::FilePath(kManifestJSONFileName), 0, &proc_handle));
// Non-existent CRX file.
EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
elevation_service::RunCRX(
TestFile("nonexistent.crx3"),
base::CommandLine(base::CommandLine::NO_PROGRAM),
crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF,
GetValidPublisherCrx3Hash(), GetUnpackDir(),
base::FilePath(kManifestJSONFileName), 0, &proc_handle));
// manifest.json is not a Windows executable, ::CreateProcess therefore
// returns ERROR_BAD_EXE_FORMAT.
EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT),
elevation_service::RunCRX(
TestFile("valid_publisher.crx3"),
base::CommandLine(base::CommandLine::NO_PROGRAM),
crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF,
GetValidPublisherCrx3Hash(), GetUnpackDir(),
base::FilePath(kManifestJSONFileName), 0, &proc_handle));
}
TEST(ElevatedRecoveryTest, Do_RunCRX_ValidArgs) {
base::win::ScopedHandle proc_handle;
// ChromeRecovery.crx3 contains ChromeRecovery.exe which returns a hardcoded
// value of 1877345072.
EXPECT_EQ(S_OK, elevation_service::RunCRX(
TestFile("ChromeRecovery.crx3"),
base::CommandLine(base::CommandLine::NO_PROGRAM),
crx_file::VerifierFormat::CRX3,
GetRunactionTestWinCrx3Hash(), GetUnpackDir(),
base::FilePath(kRecoveryExeName), 0, &proc_handle));
EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(proc_handle.Get(), 500));
DWORD exit_code = 0;
EXPECT_TRUE(::GetExitCodeProcess(proc_handle.Get(), &exit_code));
EXPECT_EQ(1877345072UL, exit_code);
}
TEST(ElevatedRecoveryTest, Do_CleanupChromeRecoveryDirectory) {
base::FilePath recovery_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &recovery_dir));
recovery_dir = recovery_dir.DirName().DirName().Append(
FILE_PATH_LITERAL("ChromeRecovery"));
ASSERT_TRUE(base::CreateDirectory(recovery_dir));
base::FilePath temp_file;
ASSERT_TRUE(base::CreateTemporaryFileInDir(recovery_dir, &temp_file));
ASSERT_TRUE(base::CreateTemporaryFileInDir(recovery_dir, &temp_file));
ASSERT_TRUE(base::CreateTemporaryFileInDir(recovery_dir, &temp_file));
base::ScopedTempDir scoped_dir;
ASSERT_TRUE(scoped_dir.CreateUniqueTempDirUnderPath(recovery_dir));
ASSERT_TRUE(base::CreateTemporaryFileInDir(scoped_dir.GetPath(), &temp_file));
ASSERT_TRUE(base::CreateTemporaryFileInDir(scoped_dir.GetPath(), &temp_file));
EXPECT_EQ(S_OK, elevation_service::CleanupChromeRecoveryDirectory());
EXPECT_TRUE(base::PathExists(recovery_dir));
EXPECT_TRUE(base::IsDirectoryEmpty(recovery_dir));
}
} // namespace elevation_service
......@@ -17,10 +17,21 @@ interface IElevator : IUnknown
// Elevators are exposed as methods on IElevator, and provide High Integrity
// actions. Any changes to add or change a method in IElevator will require a
// security review.
// ...
// For example, a method might be:
// HRESULT CopyAndVerifyComponent([in] BSTR component_id);
// ...
//
// Runs the Chrome Recovery CRX elevated.
//
// @param crx_path Path for the recovery CRX.
// @param browser_appid Omaha AppID for the version of Chrome being recovered.
// @param browser_version Version of Chrome for the recovery CRX.
// @param session_id Omaha Session Id.
// @param caller_proc_id The process id of the calling process.
// @param proc_handle The process handle valid in the calling process context.
HRESULT RunRecoveryCRXElevated([in, string] const WCHAR* crx_path,
[in, string] const WCHAR* browser_appid,
[in, string] const WCHAR* browser_version,
[in, string] const WCHAR* session_id,
[in] DWORD caller_proc_id,
[out] ULONG_PTR* proc_handle);
};
[
......
......@@ -4,8 +4,27 @@
#include "chrome/elevation_service/elevator.h"
#include <stdint.h>
#include "base/files/file_path.h"
#include "base/win/win_util.h"
#include "chrome/elevation_service/elevated_recovery_impl.h"
namespace elevation_service {
Elevator::~Elevator() = default;
STDMETHODIMP Elevator::RunRecoveryCRXElevated(
const base::char16* crx_path,
const base::char16* browser_appid,
const base::char16* browser_version,
const base::char16* session_id,
DWORD caller_proc_id,
ULONG_PTR* proc_handle) {
base::win::ScopedHandle scoped_proc_handle;
HRESULT hr = RunChromeRecoveryCRX(base::FilePath(crx_path), browser_appid,
browser_version, session_id, caller_proc_id,
&scoped_proc_handle);
*proc_handle = base::win::HandleToUint32(scoped_proc_handle.Take());
return hr;
}
} // namespace elevation_service
......@@ -11,6 +11,7 @@
#include <wrl/module.h>
#include "base/macros.h"
#include "base/strings/string16.h"
#include "chrome/elevation_service/elevation_service_idl.h"
namespace elevation_service {
......@@ -22,8 +23,19 @@ class Elevator
public:
Elevator() = default;
// Securely validates and runs the provided Chrome Recovery CRX elevated, by
// first copying the CRX to a secure directory under %ProgramFiles% to
// validate and unpack the CRX.
IFACEMETHOD(RunRecoveryCRXElevated)
(const base::char16* crx_path,
const base::char16* browser_appid,
const base::char16* browser_version,
const base::char16* session_id,
DWORD caller_proc_id,
ULONG_PTR* proc_handle);
private:
~Elevator() override;
~Elevator() override = default;
DISALLOW_COPY_AND_ASSIGN(Elevator);
};
......
......@@ -18,6 +18,7 @@
#include "base/command_line.h"
#include "base/stl_util.h"
#include "base/win/scoped_com_initializer.h"
#include "chrome/elevation_service/elevated_recovery_impl.h"
#include "chrome/elevation_service/elevator.h"
#include "chrome/install_static/install_util.h"
......@@ -99,7 +100,7 @@ HRESULT ServiceMain::RegisterClassObjects() {
hr = module.RegisterCOMObject(nullptr, class_ids, class_factories, cookies_,
base::size(cookies_));
if (FAILED(hr)) {
LOG(ERROR) << "NotificationActivator registration failed; hr: " << hr;
LOG(ERROR) << "RegisterCOMObject failed; hr: " << hr;
return hr;
}
......@@ -111,7 +112,7 @@ void ServiceMain::UnregisterClassObjects() {
const HRESULT hr =
module.UnregisterCOMObject(nullptr, cookies_, base::size(cookies_));
if (FAILED(hr))
LOG(ERROR) << "NotificationActivator unregistration failed; hr: " << hr;
LOG(ERROR) << "UnregisterCOMObject failed; hr: " << hr;
// Unregister the Elevator class factories.
UnregisterElevatorFactories();
......@@ -209,6 +210,8 @@ void ServiceMain::SetServiceStatus(DWORD state) {
}
HRESULT ServiceMain::Run() {
LOG_IF(WARNING, FAILED(CleanupChromeRecoveryDirectory()));
HRESULT hr = InitializeComSecurity();
if (FAILED(hr))
return hr;
......
......@@ -83,6 +83,14 @@ EXTERN_C const IID IID_IElevator;
IElevator : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE RunRecoveryCRXElevated(
/* [string][in] */ const WCHAR *crx_path,
/* [string][in] */ const WCHAR *browser_appid,
/* [string][in] */ const WCHAR *browser_version,
/* [string][in] */ const WCHAR *session_id,
/* [in] */ DWORD caller_proc_id,
/* [out] */ ULONG_PTR *proc_handle) = 0;
};
......@@ -104,6 +112,15 @@ EXTERN_C const IID IID_IElevator;
ULONG ( STDMETHODCALLTYPE *Release )(
IElevator * This);
HRESULT ( STDMETHODCALLTYPE *RunRecoveryCRXElevated )(
IElevator * This,
/* [string][in] */ const WCHAR *crx_path,
/* [string][in] */ const WCHAR *browser_appid,
/* [string][in] */ const WCHAR *browser_version,
/* [string][in] */ const WCHAR *session_id,
/* [in] */ DWORD caller_proc_id,
/* [out] */ ULONG_PTR *proc_handle);
END_INTERFACE
} IElevatorVtbl;
......@@ -127,6 +144,9 @@ EXTERN_C const IID IID_IElevator;
( (This)->lpVtbl -> Release(This) )
#define IElevator_RunRecoveryCRXElevated(This,crx_path,browser_appid,browser_version,session_id,caller_proc_id,proc_handle) \
( (This)->lpVtbl -> RunRecoveryCRXElevated(This,crx_path,browser_appid,browser_version,session_id,caller_proc_id,proc_handle) )
#endif /* COBJMACROS */
......
......@@ -46,8 +46,8 @@
#include "elevation_service_idl.h"
#define TYPE_FORMAT_STRING_SIZE 3
#define PROC_FORMAT_STRING_SIZE 1
#define TYPE_FORMAT_STRING_SIZE 11
#define PROC_FORMAT_STRING_SIZE 69
#define EXPR_FORMAT_STRING_SIZE 1
#define TRANSMIT_AS_TABLE_SIZE 0
#define WIRE_MARSHAL_TABLE_SIZE 0
......@@ -97,6 +97,69 @@ static const elevation_service_idl_MIDL_PROC_FORMAT_STRING elevation_service_idl
0,
{
/* Procedure RunRecoveryCRXElevated */
0x33, /* FC_AUTO_HANDLE */
0x6c, /* Old Flags: object, Oi2 */
/* 2 */ NdrFcLong( 0x0 ), /* 0 */
/* 6 */ NdrFcShort( 0x3 ), /* 3 */
/* 8 */ NdrFcShort( 0x40 ), /* X64 Stack size/offset = 64 */
/* 10 */ NdrFcShort( 0x8 ), /* 8 */
/* 12 */ NdrFcShort( 0x24 ), /* 36 */
/* 14 */ 0x46, /* Oi2 Flags: clt must size, has return, has ext, */
0x7, /* 7 */
/* 16 */ 0xa, /* 10 */
0x1, /* Ext Flags: new corr desc, */
/* 18 */ NdrFcShort( 0x0 ), /* 0 */
/* 20 */ NdrFcShort( 0x0 ), /* 0 */
/* 22 */ NdrFcShort( 0x0 ), /* 0 */
/* 24 */ NdrFcShort( 0x0 ), /* 0 */
/* Parameter crx_path */
/* 26 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 28 */ NdrFcShort( 0x8 ), /* X64 Stack size/offset = 8 */
/* 30 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter browser_appid */
/* 32 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 34 */ NdrFcShort( 0x10 ), /* X64 Stack size/offset = 16 */
/* 36 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter browser_version */
/* 38 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 40 */ NdrFcShort( 0x18 ), /* X64 Stack size/offset = 24 */
/* 42 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter session_id */
/* 44 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 46 */ NdrFcShort( 0x20 ), /* X64 Stack size/offset = 32 */
/* 48 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter caller_proc_id */
/* 50 */ NdrFcShort( 0x48 ), /* Flags: in, base type, */
/* 52 */ NdrFcShort( 0x28 ), /* X64 Stack size/offset = 40 */
/* 54 */ 0x8, /* FC_LONG */
0x0, /* 0 */
/* Parameter proc_handle */
/* 56 */ NdrFcShort( 0x2150 ), /* Flags: out, base type, simple ref, srv alloc size=8 */
/* 58 */ NdrFcShort( 0x30 ), /* X64 Stack size/offset = 48 */
/* 60 */ 0xb9, /* FC_UINT3264 */
0x0, /* 0 */
/* Return value */
/* 62 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */
/* 64 */ NdrFcShort( 0x38 ), /* X64 Stack size/offset = 56 */
/* 66 */ 0x8, /* FC_LONG */
0x0, /* 0 */
0x0
}
};
......@@ -106,6 +169,15 @@ static const elevation_service_idl_MIDL_TYPE_FORMAT_STRING elevation_service_idl
0,
{
NdrFcShort( 0x0 ), /* 0 */
/* 2 */
0x11, 0x8, /* FC_RP [simple_pointer] */
/* 4 */
0x25, /* FC_C_WSTRING */
0x5c, /* FC_PAD */
/* 6 */
0x11, 0xc, /* FC_RP [alloced_on_stack] [simple_pointer] */
/* 8 */ 0xb9, /* FC_UINT3264 */
0x5c, /* FC_PAD */
0x0
}
......@@ -146,20 +218,21 @@ static const MIDL_SERVER_INFO IElevator_ServerInfo =
0,
0,
0};
CINTERFACE_PROXY_VTABLE(3) _IElevatorProxyVtbl =
CINTERFACE_PROXY_VTABLE(4) _IElevatorProxyVtbl =
{
0,
&IElevator_ProxyInfo,
&IID_IElevator,
IUnknown_QueryInterface_Proxy,
IUnknown_AddRef_Proxy,
IUnknown_Release_Proxy
IUnknown_Release_Proxy ,
(void *) (INT_PTR) -1 /* IElevator::RunRecoveryCRXElevated */
};
const CInterfaceStubVtbl _IElevatorStubVtbl =
{
&IID_IElevator,
&IElevator_ServerInfo,
3,
4,
0, /* pure interpreted */
CStdStubBuffer_METHODS
};
......
......@@ -83,6 +83,14 @@ EXTERN_C const IID IID_IElevator;
IElevator : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE RunRecoveryCRXElevated(
/* [string][in] */ const WCHAR *crx_path,
/* [string][in] */ const WCHAR *browser_appid,
/* [string][in] */ const WCHAR *browser_version,
/* [string][in] */ const WCHAR *session_id,
/* [in] */ DWORD caller_proc_id,
/* [out] */ ULONG_PTR *proc_handle) = 0;
};
......@@ -104,6 +112,15 @@ EXTERN_C const IID IID_IElevator;
ULONG ( STDMETHODCALLTYPE *Release )(
IElevator * This);
HRESULT ( STDMETHODCALLTYPE *RunRecoveryCRXElevated )(
IElevator * This,
/* [string][in] */ const WCHAR *crx_path,
/* [string][in] */ const WCHAR *browser_appid,
/* [string][in] */ const WCHAR *browser_version,
/* [string][in] */ const WCHAR *session_id,
/* [in] */ DWORD caller_proc_id,
/* [out] */ ULONG_PTR *proc_handle);
END_INTERFACE
} IElevatorVtbl;
......@@ -127,6 +144,9 @@ EXTERN_C const IID IID_IElevator;
( (This)->lpVtbl -> Release(This) )
#define IElevator_RunRecoveryCRXElevated(This,crx_path,browser_appid,browser_version,session_id,caller_proc_id,proc_handle) \
( (This)->lpVtbl -> RunRecoveryCRXElevated(This,crx_path,browser_appid,browser_version,session_id,caller_proc_id,proc_handle) )
#endif /* COBJMACROS */
......
......@@ -49,8 +49,8 @@
#include "elevation_service_idl.h"
#define TYPE_FORMAT_STRING_SIZE 3
#define PROC_FORMAT_STRING_SIZE 1
#define TYPE_FORMAT_STRING_SIZE 11
#define PROC_FORMAT_STRING_SIZE 67
#define EXPR_FORMAT_STRING_SIZE 1
#define TRANSMIT_AS_TABLE_SIZE 0
#define WIRE_MARSHAL_TABLE_SIZE 0
......@@ -108,6 +108,68 @@ static const elevation_service_idl_MIDL_PROC_FORMAT_STRING elevation_service_idl
0,
{
/* Procedure RunRecoveryCRXElevated */
0x33, /* FC_AUTO_HANDLE */
0x6c, /* Old Flags: object, Oi2 */
/* 2 */ NdrFcLong( 0x0 ), /* 0 */
/* 6 */ NdrFcShort( 0x3 ), /* 3 */
/* 8 */ NdrFcShort( 0x20 ), /* x86 Stack size/offset = 32 */
/* 10 */ NdrFcShort( 0x8 ), /* 8 */
/* 12 */ NdrFcShort( 0x24 ), /* 36 */
/* 14 */ 0x46, /* Oi2 Flags: clt must size, has return, has ext, */
0x7, /* 7 */
/* 16 */ 0x8, /* 8 */
0x1, /* Ext Flags: new corr desc, */
/* 18 */ NdrFcShort( 0x0 ), /* 0 */
/* 20 */ NdrFcShort( 0x0 ), /* 0 */
/* 22 */ NdrFcShort( 0x0 ), /* 0 */
/* Parameter crx_path */
/* 24 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 26 */ NdrFcShort( 0x4 ), /* x86 Stack size/offset = 4 */
/* 28 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter browser_appid */
/* 30 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 32 */ NdrFcShort( 0x8 ), /* x86 Stack size/offset = 8 */
/* 34 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter browser_version */
/* 36 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 38 */ NdrFcShort( 0xc ), /* x86 Stack size/offset = 12 */
/* 40 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter session_id */
/* 42 */ NdrFcShort( 0x10b ), /* Flags: must size, must free, in, simple ref, */
/* 44 */ NdrFcShort( 0x10 ), /* x86 Stack size/offset = 16 */
/* 46 */ NdrFcShort( 0x4 ), /* Type Offset=4 */
/* Parameter caller_proc_id */
/* 48 */ NdrFcShort( 0x48 ), /* Flags: in, base type, */
/* 50 */ NdrFcShort( 0x14 ), /* x86 Stack size/offset = 20 */
/* 52 */ 0x8, /* FC_LONG */
0x0, /* 0 */
/* Parameter proc_handle */
/* 54 */ NdrFcShort( 0x2150 ), /* Flags: out, base type, simple ref, srv alloc size=8 */
/* 56 */ NdrFcShort( 0x18 ), /* x86 Stack size/offset = 24 */
/* 58 */ 0x8, /* FC_LONG */
0x0, /* 0 */
/* Return value */
/* 60 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */
/* 62 */ NdrFcShort( 0x1c ), /* x86 Stack size/offset = 28 */
/* 64 */ 0x8, /* FC_LONG */
0x0, /* 0 */
0x0
}
};
......@@ -117,6 +179,15 @@ static const elevation_service_idl_MIDL_TYPE_FORMAT_STRING elevation_service_idl
0,
{
NdrFcShort( 0x0 ), /* 0 */
/* 2 */
0x11, 0x8, /* FC_RP [simple_pointer] */
/* 4 */
0x25, /* FC_C_WSTRING */
0x5c, /* FC_PAD */
/* 6 */
0x11, 0xc, /* FC_RP [alloced_on_stack] [simple_pointer] */
/* 8 */ 0x8, /* FC_LONG */
0x5c, /* FC_PAD */
0x0
}
......@@ -157,20 +228,21 @@ static const MIDL_SERVER_INFO IElevator_ServerInfo =
0,
0,
0};
CINTERFACE_PROXY_VTABLE(3) _IElevatorProxyVtbl =
CINTERFACE_PROXY_VTABLE(4) _IElevatorProxyVtbl =
{
0,
&IElevator_ProxyInfo,
&IID_IElevator,
IUnknown_QueryInterface_Proxy,
IUnknown_AddRef_Proxy,
IUnknown_Release_Proxy
IUnknown_Release_Proxy ,
(void *) (INT_PTR) -1 /* IElevator::RunRecoveryCRXElevated */
};
const CInterfaceStubVtbl _IElevatorStubVtbl =
{
&IID_IElevator,
&IElevator_ServerInfo,
3,
4,
0, /* pure interpreted */
CStdStubBuffer_METHODS
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment