| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <wrl/client.h> |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/compiler_specific.h" |
| #include "base/containers/heap_array.h" |
| #include "base/files/file_path.h" |
| #include "base/path_service.h" |
| #include "base/test/mock_callback.h" |
| #include "base/win/scoped_bstr.h" |
| #include "base/win/scoped_com_initializer.h" |
| #include "chrome/elevation_service/elevation_service_delegate.h" |
| #include "chrome/elevation_service/elevation_service_idl.h" |
| #include "chrome/install_static/install_util.h" |
| #include "chrome/windows_services/service_program/process_wrl_module.h" |
| #include "chrome/windows_services/service_program/service.h" |
| #include "chrome/windows_services/service_program/test_support/scoped_mock_context.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| 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 |
| |
| class ServiceMainTest : public testing::Test { |
| protected: |
| ServiceMainTest() = default; |
| ~ServiceMainTest() override { Service::UnregisterClassObjects(cookies_); } |
| |
| void SetUp() override { |
| ASSERT_TRUE(com_initializer_.Succeeded()); |
| |
| ASSERT_HRESULT_SUCCEEDED(Service::RegisterClassObjects( |
| service_delegate_, module_released_callback_.Get(), cookies_)); |
| } |
| |
| base::MockCallback<base::OnceClosure>& mock_module_released_callback() { |
| return module_released_callback_; |
| } |
| |
| private: |
| base::win::ScopedCOMInitializer com_initializer_; |
| ::testing::NiceMock<base::MockCallback<base::OnceClosure>> |
| module_released_callback_; |
| elevation_service::Delegate service_delegate_; |
| base::HeapArray<DWORD> cookies_; |
| }; |
| |
| TEST_F(ServiceMainTest, ExitSignalTest) { |
| auto& module_released_callback = mock_module_released_callback(); |
| ScopedMockContext mock_context; |
| ASSERT_TRUE(mock_context.Succeeded()); |
| |
| Microsoft::WRL::ComPtr<IUnknown> unknown; |
| ASSERT_HRESULT_SUCCEEDED( |
| ::CoCreateInstance(install_static::GetElevatorClsid(), nullptr, |
| CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&unknown))); |
| |
| Microsoft::WRL::ComPtr<IElevator> elevator; |
| ASSERT_HRESULT_SUCCEEDED(unknown.As(&elevator)); |
| unknown.Reset(); |
| |
| ULONG_PTR proc_handle = 0; |
| EXPECT_EQ(CRYPT_E_NO_MATCH, |
| elevator->RunRecoveryCRXElevated( |
| TestFile("ChromeRecovery.crx3").value().c_str(), |
| L"{c49ab053-2387-4809-b188-1902648802e1}", L"57.8.0.1", |
| L"{c49ab053-2387-4809-b188-1902648802e1}", |
| ::GetCurrentProcessId(), &proc_handle)); |
| |
| // An object instance has been created upon the request, and is held by the |
| // server module. Therefore, the callback has not yet run. |
| ::testing::Mock::VerifyAndClearExpectations(&module_released_callback); |
| |
| // Release the instance object. Now that the last (and the only) instance |
| // object of the module is released, the event becomes signaled. |
| EXPECT_CALL(module_released_callback, Run()); |
| elevator.Reset(); |
| } |
| |
| TEST_F(ServiceMainTest, EncryptDecryptTest) { |
| ScopedMockContext mock_context; |
| ASSERT_TRUE(mock_context.Succeeded()); |
| |
| Microsoft::WRL::ComPtr<IUnknown> unknown; |
| ASSERT_HRESULT_SUCCEEDED( |
| ::CoCreateInstance(install_static::GetElevatorClsid(), nullptr, |
| CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&unknown))); |
| |
| Microsoft::WRL::ComPtr<IElevator> elevator; |
| ASSERT_HRESULT_SUCCEEDED(unknown.As(&elevator)); |
| unknown.Reset(); |
| |
| std::string plaintext("hello world"); |
| BSTR input = ::SysAllocStringByteLen(nullptr, plaintext.length()); |
| ASSERT_TRUE(input); |
| |
| UNSAFE_TODO(memcpy(input, plaintext.data(), plaintext.length())); |
| base::win::ScopedBstr output; |
| DWORD last_error; |
| HRESULT hr = |
| elevator->EncryptData(ProtectionLevel::PROTECTION_PATH_VALIDATION, input, |
| output.Receive(), &last_error); |
| ::SysFreeString(input); |
| |
| ASSERT_HRESULT_SUCCEEDED(hr); |
| |
| std::string encrypted; |
| encrypted.assign(reinterpret_cast<const char*>(output.Get()), |
| output.ByteLength()); |
| |
| BSTR input2 = ::SysAllocStringByteLen(nullptr, encrypted.length()); |
| UNSAFE_TODO(memcpy(input2, encrypted.data(), encrypted.length())); |
| base::win::ScopedBstr original; |
| |
| hr = elevator->DecryptData(input2, original.Receive(), &last_error); |
| |
| ::SysFreeString(input); |
| |
| ASSERT_HRESULT_SUCCEEDED(hr); |
| |
| elevator.Reset(); |
| |
| std::string original_string; |
| original_string.assign(reinterpret_cast<const char*>(original.Get()), |
| original.ByteLength()); |
| |
| ASSERT_EQ(plaintext.length(), original_string.length()); |
| ASSERT_EQ(plaintext, original_string); |
| } |