service_main.cc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. // This macro is used in <wrl/module.h>. Since only the COM functionality is
  5. // used here (while WinRT isn't being used), define this macro to optimize
  6. // compilation of <wrl/module.h> for COM-only.
  7. #ifndef __WRL_CLASSIC_COM_STRICT__
  8. #define __WRL_CLASSIC_COM_STRICT__
  9. #endif // __WRL_CLASSIC_COM_STRICT__
  10. #include "chrome/elevation_service/service_main.h"
  11. #include <atlsecurity.h>
  12. #include <sddl.h>
  13. #include <wrl/module.h>
  14. #include <type_traits>
  15. #include "base/command_line.h"
  16. #include "base/logging.h"
  17. #include "base/no_destructor.h"
  18. #include "base/win/scoped_com_initializer.h"
  19. #include "chrome/elevation_service/elevated_recovery_impl.h"
  20. #include "chrome/elevation_service/elevator.h"
  21. #include "chrome/install_static/install_util.h"
  22. namespace elevation_service {
  23. namespace {
  24. // Command line switch "--console" runs the service interactively for
  25. // debugging purposes.
  26. constexpr char kConsoleSwitchName[] = "console";
  27. constexpr wchar_t kWindowsServiceName[] = L"ChromeElevationService";
  28. } // namespace
  29. ServiceMain* ServiceMain::GetInstance() {
  30. static base::NoDestructor<ServiceMain> instance;
  31. return instance.get();
  32. }
  33. bool ServiceMain::InitWithCommandLine(const base::CommandLine* command_line) {
  34. const base::CommandLine::StringVector args = command_line->GetArgs();
  35. if (!args.empty()) {
  36. LOG(ERROR) << "No positional parameters expected.";
  37. return false;
  38. }
  39. // Run interactively if needed.
  40. if (command_line->HasSwitch(kConsoleSwitchName))
  41. run_routine_ = &ServiceMain::RunInteractive;
  42. return true;
  43. }
  44. // Start() is the entry point called by WinMain.
  45. int ServiceMain::Start() {
  46. return (this->*run_routine_)();
  47. }
  48. void ServiceMain::CreateWRLModule() {
  49. Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::Create(
  50. this, &ServiceMain::SignalExit);
  51. }
  52. // When _ServiceMain gets called, it initializes COM, and then calls Run().
  53. // Run() initializes security, then calls RegisterClassObject().
  54. HRESULT ServiceMain::RegisterClassObject() {
  55. auto& module = Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule();
  56. // We hand-register a unique CLSID for each Chrome channel.
  57. Microsoft::WRL::ComPtr<IUnknown> factory;
  58. unsigned int flags = Microsoft::WRL::ModuleType::OutOfProc;
  59. HRESULT hr = Microsoft::WRL::Details::CreateClassFactory<
  60. Microsoft::WRL::SimpleClassFactory<Elevator>>(
  61. &flags, nullptr, __uuidof(IClassFactory), &factory);
  62. if (FAILED(hr)) {
  63. LOG(ERROR) << "Factory creation failed; hr: " << hr;
  64. return hr;
  65. }
  66. Microsoft::WRL::ComPtr<IClassFactory> class_factory;
  67. hr = factory.As(&class_factory);
  68. if (FAILED(hr)) {
  69. LOG(ERROR) << "IClassFactory object creation failed; hr: " << hr;
  70. return hr;
  71. }
  72. // The pointer in this array is unowned. Do not release it.
  73. IClassFactory* class_factories[] = {class_factory.Get()};
  74. static_assert(std::extent<decltype(cookies_)>() == std::size(class_factories),
  75. "Arrays cookies_ and class_factories must be the same size.");
  76. IID class_ids[] = {install_static::GetElevatorClsid()};
  77. if (base::CommandLine::ForCurrentProcess()->HasSwitch(
  78. switches::kElevatorClsIdForTestingSwitch)) {
  79. class_ids[0] = {kTestElevatorClsid};
  80. }
  81. DCHECK_EQ(std::size(cookies_), std::size(class_ids));
  82. static_assert(std::extent<decltype(cookies_)>() == std::size(class_ids),
  83. "Arrays cookies_ and class_ids must be the same size.");
  84. hr = module.RegisterCOMObject(nullptr, class_ids, class_factories, cookies_,
  85. std::size(cookies_));
  86. if (FAILED(hr)) {
  87. LOG(ERROR) << "RegisterCOMObject failed; hr: " << hr;
  88. return hr;
  89. }
  90. return hr;
  91. }
  92. void ServiceMain::UnregisterClassObject() {
  93. auto& module = Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule();
  94. const HRESULT hr =
  95. module.UnregisterCOMObject(nullptr, cookies_, std::size(cookies_));
  96. if (FAILED(hr))
  97. LOG(ERROR) << "UnregisterCOMObject failed; hr: " << hr;
  98. }
  99. bool ServiceMain::IsExitSignaled() {
  100. return exit_signal_.IsSignaled();
  101. }
  102. void ServiceMain::ResetExitSignaled() {
  103. exit_signal_.Reset();
  104. }
  105. ServiceMain::ServiceMain()
  106. : run_routine_(&ServiceMain::RunAsService),
  107. service_status_handle_(nullptr),
  108. service_status_(),
  109. cookies_(),
  110. exit_signal_(base::WaitableEvent::ResetPolicy::MANUAL,
  111. base::WaitableEvent::InitialState::NOT_SIGNALED) {
  112. service_status_.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  113. service_status_.dwCurrentState = SERVICE_STOPPED;
  114. service_status_.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  115. }
  116. ServiceMain::~ServiceMain() = default;
  117. int ServiceMain::RunAsService() {
  118. static constexpr SERVICE_TABLE_ENTRY dispatch_table[] = {
  119. {const_cast<LPTSTR>(kWindowsServiceName), &ServiceMain::ServiceMainEntry},
  120. {nullptr, nullptr}};
  121. if (!::StartServiceCtrlDispatcher(dispatch_table)) {
  122. service_status_.dwWin32ExitCode = ::GetLastError();
  123. PLOG(ERROR) << "Failed to connect to the service control manager";
  124. }
  125. return service_status_.dwWin32ExitCode;
  126. }
  127. void ServiceMain::ServiceMainImpl() {
  128. service_status_handle_ = ::RegisterServiceCtrlHandler(
  129. kWindowsServiceName, &ServiceMain::ServiceControlHandler);
  130. if (service_status_handle_ == nullptr) {
  131. PLOG(ERROR) << "RegisterServiceCtrlHandler failed";
  132. return;
  133. }
  134. SetServiceStatus(SERVICE_RUNNING);
  135. service_status_.dwWin32ExitCode = ERROR_SUCCESS;
  136. service_status_.dwCheckPoint = 0;
  137. service_status_.dwWaitHint = 0;
  138. // Initialize COM for the current thread.
  139. base::win::ScopedCOMInitializer com_initializer(
  140. base::win::ScopedCOMInitializer::kMTA);
  141. if (!com_initializer.Succeeded()) {
  142. PLOG(ERROR) << "Failed to initialize COM";
  143. SetServiceStatus(SERVICE_STOPPED);
  144. return;
  145. }
  146. // When the Run function returns, the service has stopped.
  147. const HRESULT hr = Run();
  148. if (FAILED(hr)) {
  149. service_status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  150. service_status_.dwServiceSpecificExitCode = hr;
  151. }
  152. SetServiceStatus(SERVICE_STOPPED);
  153. }
  154. int ServiceMain::RunInteractive() {
  155. return Run();
  156. }
  157. // static
  158. void ServiceMain::ServiceControlHandler(DWORD control) {
  159. ServiceMain* self = ServiceMain::GetInstance();
  160. switch (control) {
  161. case SERVICE_CONTROL_STOP:
  162. self->SetServiceStatus(SERVICE_STOP_PENDING);
  163. self->SignalExit();
  164. break;
  165. default:
  166. break;
  167. }
  168. }
  169. // static
  170. void WINAPI ServiceMain::ServiceMainEntry(DWORD argc, wchar_t* argv[]) {
  171. ServiceMain::GetInstance()->ServiceMainImpl();
  172. }
  173. void ServiceMain::SetServiceStatus(DWORD state) {
  174. ::InterlockedExchange(&service_status_.dwCurrentState, state);
  175. ::SetServiceStatus(service_status_handle_, &service_status_);
  176. }
  177. HRESULT ServiceMain::Run() {
  178. LOG_IF(WARNING, FAILED(CleanupChromeRecoveryDirectory()));
  179. HRESULT hr = InitializeComSecurity();
  180. if (FAILED(hr))
  181. return hr;
  182. CreateWRLModule();
  183. hr = RegisterClassObject();
  184. if (SUCCEEDED(hr)) {
  185. WaitForExitSignal();
  186. UnregisterClassObject();
  187. }
  188. return hr;
  189. }
  190. // static
  191. HRESULT ServiceMain::InitializeComSecurity() {
  192. CDacl dacl;
  193. constexpr BYTE com_rights_execute_local =
  194. COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
  195. dacl.AddAllowedAce(Sids::System(), com_rights_execute_local);
  196. dacl.AddAllowedAce(Sids::Admins(), com_rights_execute_local);
  197. dacl.AddAllowedAce(Sids::Interactive(), com_rights_execute_local);
  198. CSecurityDesc sd;
  199. sd.SetDacl(dacl);
  200. sd.MakeAbsolute();
  201. sd.SetOwner(Sids::Admins());
  202. sd.SetGroup(Sids::Admins());
  203. return ::CoInitializeSecurity(
  204. const_cast<SECURITY_DESCRIPTOR*>(sd.GetPSECURITY_DESCRIPTOR()), -1,
  205. nullptr, nullptr, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY,
  206. nullptr, EOAC_DYNAMIC_CLOAKING | EOAC_NO_CUSTOM_MARSHAL, nullptr);
  207. }
  208. void ServiceMain::WaitForExitSignal() {
  209. exit_signal_.Wait();
  210. }
  211. void ServiceMain::SignalExit() {
  212. exit_signal_.Signal();
  213. }
  214. } // namespace elevation_service