| // 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. |
| |
| // This macro is used in <wrl/module.h>. Since only the COM functionality is |
| // used here (while WinRT isn't being used), define this macro to optimize |
| // compilation of <wrl/module.h> for COM-only. |
| #ifndef __WRL_CLASSIC_COM_STRICT__ |
| #define __WRL_CLASSIC_COM_STRICT__ |
| #endif // __WRL_CLASSIC_COM_STRICT__ |
| |
| #include "chrome/elevation_service/service_main.h" |
| |
| #include <type_traits> |
| |
| #include <atlsecurity.h> |
| #include <sddl.h> |
| #include <wrl/module.h> |
| |
| #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" |
| |
| namespace elevation_service { |
| |
| namespace { |
| |
| // Command line switch "--console" runs the service interactively for |
| // debugging purposes. |
| constexpr char kConsoleSwitchName[] = "console"; |
| constexpr base::char16 kWindowsServiceName[] = L"ChromeElevationService"; |
| |
| } // namespace |
| |
| ServiceMain* ServiceMain::GetInstance() { |
| static base::NoDestructor<ServiceMain> instance; |
| return instance.get(); |
| } |
| |
| bool ServiceMain::InitWithCommandLine(const base::CommandLine* command_line) { |
| const base::CommandLine::StringVector args = command_line->GetArgs(); |
| if (!args.empty()) { |
| LOG(ERROR) << "No positional parameters expected."; |
| return false; |
| } |
| |
| // Run interactively if needed. |
| if (command_line->HasSwitch(kConsoleSwitchName)) |
| run_routine_ = &ServiceMain::RunInteractive; |
| |
| return true; |
| } |
| |
| // Start() is the entry point called by WinMain. |
| int ServiceMain::Start() { |
| return (this->*run_routine_)(); |
| } |
| |
| // When _ServiceMain gets called, it initializes COM, and then calls Run(). |
| // Run() initializes security, then calls RegisterClassObject(). |
| HRESULT ServiceMain::RegisterClassObject() { |
| // Create an out-of-proc COM module with caching disabled. |
| auto& module = Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::Create( |
| this, &ServiceMain::SignalExit); |
| |
| // We hand-register a unique CLSID for each Chrome channel. |
| Microsoft::WRL::ComPtr<IUnknown> factory; |
| unsigned int flags = Microsoft::WRL::ModuleType::OutOfProc; |
| |
| HRESULT hr = Microsoft::WRL::Details::CreateClassFactory< |
| Microsoft::WRL::SimpleClassFactory<Elevator>>( |
| &flags, nullptr, __uuidof(IClassFactory), &factory); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Factory creation failed; hr: " << hr; |
| return hr; |
| } |
| |
| Microsoft::WRL::ComPtr<IClassFactory> class_factory; |
| hr = factory.As(&class_factory); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "IClassFactory object creation failed; hr: " << hr; |
| return hr; |
| } |
| |
| // The pointer in this array is unowned. Do not release it. |
| IClassFactory* class_factories[] = {class_factory.Get()}; |
| static_assert( |
| std::extent<decltype(cookies_)>() == base::size(class_factories), |
| "Arrays cookies_ and class_factories must be the same size."); |
| |
| IID class_ids[] = {install_static::GetElevatorClsid()}; |
| DCHECK_EQ(base::size(cookies_), base::size(class_ids)); |
| static_assert(std::extent<decltype(cookies_)>() == base::size(class_ids), |
| "Arrays cookies_ and class_ids must be the same size."); |
| |
| hr = module.RegisterCOMObject(nullptr, class_ids, class_factories, cookies_, |
| base::size(cookies_)); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "RegisterCOMObject failed; hr: " << hr; |
| return hr; |
| } |
| |
| return hr; |
| } |
| |
| void ServiceMain::UnregisterClassObject() { |
| auto& module = Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule(); |
| const HRESULT hr = |
| module.UnregisterCOMObject(nullptr, cookies_, base::size(cookies_)); |
| if (FAILED(hr)) |
| LOG(ERROR) << "UnregisterCOMObject failed; hr: " << hr; |
| } |
| |
| bool ServiceMain::IsExitSignaled() { |
| return exit_signal_.IsSignaled(); |
| } |
| |
| ServiceMain::ServiceMain() |
| : run_routine_(&ServiceMain::RunAsService), |
| service_status_handle_(nullptr), |
| service_status_(), |
| cookies_(), |
| exit_signal_(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED) { |
| service_status_.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
| service_status_.dwCurrentState = SERVICE_STOPPED; |
| service_status_.dwControlsAccepted = SERVICE_ACCEPT_STOP; |
| } |
| |
| ServiceMain::~ServiceMain() = default; |
| |
| int ServiceMain::RunAsService() { |
| static constexpr SERVICE_TABLE_ENTRY dispatch_table[] = { |
| {const_cast<LPTSTR>(kWindowsServiceName), &ServiceMain::ServiceMainEntry}, |
| {nullptr, nullptr}}; |
| |
| if (!::StartServiceCtrlDispatcher(dispatch_table)) { |
| service_status_.dwWin32ExitCode = ::GetLastError(); |
| PLOG(ERROR) << "Failed to connect to the service control manager"; |
| } |
| |
| return service_status_.dwWin32ExitCode; |
| } |
| |
| void ServiceMain::ServiceMainImpl() { |
| service_status_handle_ = ::RegisterServiceCtrlHandler( |
| kWindowsServiceName, &ServiceMain::ServiceControlHandler); |
| if (service_status_handle_ == nullptr) { |
| PLOG(ERROR) << "RegisterServiceCtrlHandler failed"; |
| return; |
| } |
| SetServiceStatus(SERVICE_RUNNING); |
| |
| service_status_.dwWin32ExitCode = ERROR_SUCCESS; |
| service_status_.dwCheckPoint = 0; |
| service_status_.dwWaitHint = 0; |
| |
| // Initialize COM for the current thread. |
| base::win::ScopedCOMInitializer com_initializer( |
| base::win::ScopedCOMInitializer::kMTA); |
| if (!com_initializer.Succeeded()) { |
| PLOG(ERROR) << "Failed to initialize COM"; |
| SetServiceStatus(SERVICE_STOPPED); |
| return; |
| } |
| |
| // When the Run function returns, the service has stopped. |
| const HRESULT hr = Run(); |
| if (FAILED(hr)) { |
| service_status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; |
| service_status_.dwServiceSpecificExitCode = hr; |
| } |
| |
| SetServiceStatus(SERVICE_STOPPED); |
| } |
| |
| int ServiceMain::RunInteractive() { |
| return Run(); |
| } |
| |
| // static |
| void ServiceMain::ServiceControlHandler(DWORD control) { |
| ServiceMain* self = ServiceMain::GetInstance(); |
| switch (control) { |
| case SERVICE_CONTROL_STOP: |
| self->SetServiceStatus(SERVICE_STOP_PENDING); |
| self->SignalExit(); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| // static |
| void WINAPI ServiceMain::ServiceMainEntry(DWORD argc, base::char16* argv[]) { |
| ServiceMain::GetInstance()->ServiceMainImpl(); |
| } |
| |
| void ServiceMain::SetServiceStatus(DWORD state) { |
| ::InterlockedExchange(&service_status_.dwCurrentState, state); |
| ::SetServiceStatus(service_status_handle_, &service_status_); |
| } |
| |
| HRESULT ServiceMain::Run() { |
| LOG_IF(WARNING, FAILED(CleanupChromeRecoveryDirectory())); |
| |
| HRESULT hr = InitializeComSecurity(); |
| if (FAILED(hr)) |
| return hr; |
| |
| hr = RegisterClassObject(); |
| if (SUCCEEDED(hr)) { |
| WaitForExitSignal(); |
| UnregisterClassObject(); |
| } |
| |
| return hr; |
| } |
| |
| // static |
| HRESULT ServiceMain::InitializeComSecurity() { |
| CDacl dacl; |
| constexpr BYTE com_rights_execute_local = |
| COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL; |
| dacl.AddAllowedAce(Sids::System(), com_rights_execute_local); |
| dacl.AddAllowedAce(Sids::Admins(), com_rights_execute_local); |
| dacl.AddAllowedAce(Sids::Interactive(), com_rights_execute_local); |
| |
| CSecurityDesc sd; |
| sd.SetDacl(dacl); |
| sd.MakeAbsolute(); |
| sd.SetOwner(Sids::Admins()); |
| sd.SetGroup(Sids::Admins()); |
| |
| return ::CoInitializeSecurity( |
| const_cast<SECURITY_DESCRIPTOR*>(sd.GetPSECURITY_DESCRIPTOR()), -1, |
| nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, |
| nullptr, EOAC_DYNAMIC_CLOAKING | EOAC_NO_CUSTOM_MARSHAL, nullptr); |
| } |
| |
| void ServiceMain::WaitForExitSignal() { |
| exit_signal_.Wait(); |
| } |
| |
| void ServiceMain::SignalExit() { |
| exit_signal_.Signal(); |
| } |
| |
| } // namespace elevation_service |