| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include "omaha/common/goopdate_utils.h" | |
| 17 #include <atlsecurity.h> | |
| 18 #include "omaha/base/app_util.h" | |
| 19 #include "omaha/base/const_addresses.h" | |
| 20 #include "omaha/base/const_object_names.h" | |
| 21 #include "omaha/base/const_utils.h" | |
| 22 #include "omaha/base/debug.h" | |
| 23 #include "omaha/base/error.h" | |
| 24 #include "omaha/base/file.h" | |
| 25 #include "omaha/base/logging.h" | |
| 26 #include "omaha/base/omaha_version.h" | |
| 27 #include "omaha/base/path.h" | |
| 28 #include "omaha/base/proc_utils.h" | |
| 29 #include "omaha/base/process.h" | |
| 30 #include "omaha/base/reg_key.h" | |
| 31 #include "omaha/base/safe_format.h" | |
| 32 #include "omaha/base/scope_guard.h" | |
| 33 #include "omaha/base/scoped_impersonation.h" | |
| 34 #include "omaha/base/service_utils.h" | |
| 35 #include "omaha/base/string.h" | |
| 36 #include "omaha/base/system.h" | |
| 37 #include "omaha/base/system_info.h" | |
| 38 #include "omaha/base/user_info.h" | |
| 39 #include "omaha/base/utils.h" | |
| 40 #include "omaha/base/vista_utils.h" | |
| 41 #include "omaha/base/vistautil.h" | |
| 42 #include "omaha/common/command_line_builder.h" | |
| 43 #include "omaha/common/config_manager.h" | |
| 44 #include "omaha/common/const_cmd_line.h" | |
| 45 #include "omaha/common/const_goopdate.h" | |
| 46 #include "omaha/common/oem_install_utils.h" | |
| 47 #include "omaha/statsreport/metrics.h" | |
| 48 #include "goopdate/omaha3_idl.h" | |
| 49 | |
| 50 namespace omaha { | |
| 51 | |
| 52 namespace goopdate_utils { | |
| 53 | |
| 54 namespace { | |
| 55 | |
| 56 const int kTerminateBrowserTimeoutMs = 60000; | |
| 57 | |
| 58 bool IsMachineProcessWithoutPrivileges(bool is_machine_process) { | |
| 59 return is_machine_process && !vista_util::IsUserAdmin(); | |
| 60 } | |
| 61 | |
| 62 HRESULT LaunchImpersonatedCmdLine(const CString& cmd_line) { | |
| 63 CORE_LOG(L3, (_T("[LaunchImpersonatedCmdLine][%s]"), cmd_line)); | |
| 64 | |
| 65 scoped_handle impersonation_token; | |
| 66 HRESULT hr = vista::GetLoggedOnUserToken(address(impersonation_token)); | |
| 67 if (FAILED(hr)) { | |
| 68 UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr)); | |
| 69 return hr; | |
| 70 } | |
| 71 | |
| 72 scoped_impersonation impersonate_user(get(impersonation_token)); | |
| 73 hr = HRESULT_FROM_WIN32(impersonate_user.result()); | |
| 74 if (FAILED(hr)) { | |
| 75 UTIL_LOG(LE, (_T("[impersonation failed][0x%x]"), hr)); | |
| 76 return hr; | |
| 77 } | |
| 78 | |
| 79 CComPtr<IProcessLauncher> launcher; | |
| 80 hr = launcher.CoCreateInstance(CLSID_ProcessLauncherClass, | |
| 81 NULL, | |
| 82 CLSCTX_LOCAL_SERVER); | |
| 83 if (FAILED(hr)) { | |
| 84 UTIL_LOG(LE, (_T("[CoCreateInstance IProcessLauncher failed][0x%x]"), hr)); | |
| 85 return hr; | |
| 86 } | |
| 87 | |
| 88 hr = launcher->LaunchCmdLine(cmd_line); | |
| 89 if (FAILED(hr)) { | |
| 90 UTIL_LOG(LE, (_T("[IProcessLauncher.LaunchBrowser failed][0x%x]"), hr)); | |
| 91 return hr; | |
| 92 } | |
| 93 | |
| 94 return S_OK; | |
| 95 } | |
| 96 | |
| 97 HRESULT LaunchImpersonatedBrowser(BrowserType type, const CString& url) { | |
| 98 CORE_LOG(L3, (_T("[LaunchImpersonatedBrowser][%u][%s]"), type, url)); | |
| 99 | |
| 100 scoped_handle impersonation_token; | |
| 101 HRESULT hr = vista::GetLoggedOnUserToken(address(impersonation_token)); | |
| 102 if (FAILED(hr)) { | |
| 103 UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr)); | |
| 104 return hr; | |
| 105 } | |
| 106 | |
| 107 scoped_impersonation impersonate_user(get(impersonation_token)); | |
| 108 hr = HRESULT_FROM_WIN32(impersonate_user.result()); | |
| 109 if (FAILED(hr)) { | |
| 110 UTIL_LOG(LE, (_T("[impersonation failed][0x%x]"), hr)); | |
| 111 return hr; | |
| 112 } | |
| 113 | |
| 114 CComPtr<IProcessLauncher> launcher; | |
| 115 hr = launcher.CoCreateInstance(CLSID_ProcessLauncherClass, | |
| 116 NULL, | |
| 117 CLSCTX_LOCAL_SERVER); | |
| 118 if (FAILED(hr)) { | |
| 119 UTIL_LOG(LE, (_T("[CoCreateInstance IProcessLauncher failed][0x%x]"), hr)); | |
| 120 return hr; | |
| 121 } | |
| 122 | |
| 123 hr = launcher->LaunchBrowser(type, url); | |
| 124 if (FAILED(hr)) { | |
| 125 UTIL_LOG(LE, (_T("[IProcessLauncher.LaunchBrowser failed][0x%x]"), hr)); | |
| 126 return hr; | |
| 127 } | |
| 128 | |
| 129 return S_OK; | |
| 130 } | |
| 131 | |
| 132 } // namespace | |
| 133 | |
| 134 HRESULT LaunchCmdLine(bool is_machine, const CString& cmd_line) { | |
| 135 CORE_LOG(L3, (_T("[LaunchCmdLine][%d][%s]"), is_machine, cmd_line)); | |
| 136 | |
| 137 if (is_machine && vista_util::IsVistaOrLater() && vista_util::IsUserAdmin()) { | |
| 138 return LaunchImpersonatedCmdLine(cmd_line); | |
| 139 } | |
| 140 | |
| 141 HRESULT hr = System::ShellExecuteCommandLine(cmd_line, NULL, NULL); | |
| 142 if (FAILED(hr)) { | |
| 143 UTIL_LOG(LE, (_T("[ShellExecuteCommandLine failed][0x%x]"), hr)); | |
| 144 return hr; | |
| 145 } | |
| 146 | |
| 147 return S_OK; | |
| 148 } | |
| 149 | |
| 150 HRESULT LaunchBrowser(bool is_machine, BrowserType type, const CString& url) { | |
| 151 CORE_LOG(L3, (_T("[LaunchBrowser][%d][%u][%s]"), is_machine, type, url)); | |
| 152 | |
| 153 if (is_machine && vista_util::IsVistaOrLater() && vista_util::IsUserAdmin()) { | |
| 154 // Other than having a service launch the browser using CreateProcessAsUser, | |
| 155 // there is no easy solution if we are unable to launch the browser | |
| 156 // impersonated. | |
| 157 return LaunchImpersonatedBrowser(type, url); | |
| 158 } | |
| 159 | |
| 160 HRESULT hr = RunBrowser(type, url); | |
| 161 if (FAILED(hr)) { | |
| 162 UTIL_LOG(LE, (_T("[RunBrowser failed][0x%x]"), hr)); | |
| 163 return hr; | |
| 164 } | |
| 165 | |
| 166 return S_OK; | |
| 167 } | |
| 168 | |
| 169 CString BuildGoogleUpdateExeDir(bool is_machine) { | |
| 170 ConfigManager& cm = *ConfigManager::Instance(); | |
| 171 return is_machine ? cm.GetMachineGoopdateInstallDir() : | |
| 172 cm.GetUserGoopdateInstallDir(); | |
| 173 } | |
| 174 | |
| 175 CString BuildGoogleUpdateExePath(bool is_machine) { | |
| 176 CORE_LOG(L3, (_T("[BuildGoogleUpdateExePath][%d]"), is_machine)); | |
| 177 | |
| 178 CPath full_file_path(BuildGoogleUpdateExeDir(is_machine)); | |
| 179 VERIFY1(full_file_path.Append(kOmahaShellFileName)); | |
| 180 | |
| 181 return full_file_path; | |
| 182 } | |
| 183 | |
| 184 CString BuildGoogleUpdateServicesPath(bool is_machine) { | |
| 185 CORE_LOG(L3, (_T("[BuildGoogleUpdateServicesPath][%d]"), is_machine)); | |
| 186 | |
| 187 CPath full_file_path(BuildInstallDirectory(is_machine, GetVersionString())); | |
| 188 VERIFY1(full_file_path.Append(kCrashHandlerFileName)); | |
| 189 | |
| 190 return full_file_path; | |
| 191 } | |
| 192 | |
| 193 HRESULT StartElevatedSelfWithArgsAndWait(const TCHAR* args, DWORD* exit_code) { | |
| 194 ASSERT1(args); | |
| 195 ASSERT1(exit_code); | |
| 196 CORE_LOG(L3, (_T("[StartElevatedSelfWithArgsAndWait]"))); | |
| 197 | |
| 198 // Get the process executable. | |
| 199 TCHAR filename[MAX_PATH] = {0}; | |
| 200 if (::GetModuleFileName(NULL, filename, MAX_PATH) == 0) { | |
| 201 HRESULT hr = HRESULTFromLastError(); | |
| 202 CORE_LOG(LEVEL_ERROR, (_T("[GetModuleFileName failed][0x%08x]"), hr)); | |
| 203 return hr; | |
| 204 } | |
| 205 | |
| 206 // Launch self elevated and wait. | |
| 207 *exit_code = 0; | |
| 208 CORE_LOG(L1, | |
| 209 (_T("[RunElevated filename='%s'][arguments='%s']"), filename, args)); | |
| 210 // According to the MSDN documentation for ::ShowWindow: "nCmdShow. This | |
| 211 // parameter is ignored the first time an application calls ShowWindow, if | |
| 212 // the program that launched the application provides a STARTUPINFO | |
| 213 // structure.". We want to force showing the UI window. So we pass in | |
| 214 // SW_SHOWNORMAL. | |
| 215 HRESULT hr(vista_util::RunElevated(filename, args, SW_SHOWNORMAL, exit_code)); | |
| 216 CORE_LOG(L2, (_T("[elevated instance exit code][%u]"), *exit_code)); | |
| 217 if (FAILED(hr)) { | |
| 218 CORE_LOG(LEVEL_ERROR, (_T("[RunElevated failed][0x%08x]"), hr)); | |
| 219 return hr; | |
| 220 } | |
| 221 | |
| 222 return S_OK; | |
| 223 } | |
| 224 | |
| 225 HRESULT StartGoogleUpdateWithArgs(bool is_machine, | |
| 226 const TCHAR* args, | |
| 227 HANDLE* process) { | |
| 228 CORE_LOG(L3, (_T("[StartGoogleUpdateWithArgs][%d][%s]"), | |
| 229 is_machine, args ? args : _T(""))); | |
| 230 | |
| 231 CString exe_path = BuildGoogleUpdateExePath(is_machine); | |
| 232 | |
| 233 CORE_LOG(L3, (_T("[command line][%s][%s]"), exe_path, args ? args : _T(""))); | |
| 234 | |
| 235 HRESULT hr = System::ShellExecuteProcess(exe_path, args, NULL, process); | |
| 236 if (FAILED(hr)) { | |
| 237 CORE_LOG(LE, (_T("[can't start process][%s][0x%08x]"), exe_path, hr)); | |
| 238 return hr; | |
| 239 } | |
| 240 return S_OK; | |
| 241 } | |
| 242 | |
| 243 HRESULT StartCrashHandler(bool is_machine) { | |
| 244 CORE_LOG(L3, (_T("[StartCrashHandler]"))); | |
| 245 | |
| 246 ASSERT1(!is_machine || user_info::IsRunningAsSystem()); | |
| 247 | |
| 248 CString exe_path = BuildGoogleUpdateServicesPath(is_machine); | |
| 249 CommandLineBuilder builder(COMMANDLINE_MODE_CRASH_HANDLER); | |
| 250 CString cmd_line = builder.GetCommandLineArgs(); | |
| 251 return System::StartProcessWithArgs(exe_path, cmd_line); | |
| 252 } | |
| 253 | |
| 254 bool IsRunningFromOfficialGoopdateDir(bool is_machine) { | |
| 255 const ConfigManager& cm = *ConfigManager::Instance(); | |
| 256 bool is_official_dir = is_machine ? | |
| 257 cm.IsRunningFromMachineGoopdateInstallDir() : | |
| 258 cm.IsRunningFromUserGoopdateInstallDir(); | |
| 259 CORE_LOG(L3, (_T("[running from official dir][%d]"), is_official_dir)); | |
| 260 return is_official_dir; | |
| 261 } | |
| 262 | |
| 263 CString GetHKRoot() { | |
| 264 return IsRunningFromOfficialGoopdateDir(true) ? _T("HKLM") : _T("HKCU"); | |
| 265 } | |
| 266 | |
| 267 HRESULT InitializeSecurity() { | |
| 268 // Creates a security descriptor in absolute format and includes the owner | |
| 269 // and the primary group. We grant access to admins and system. | |
| 270 CSecurityDesc security_descriptor; | |
| 271 if (SystemInfo::IsRunningOnVistaOrLater()) { | |
| 272 // To allow for low-integrity IE to call into IGoogleUpdate. | |
| 273 security_descriptor.FromString(LOW_INTEGRITY_SDDL_SACL); | |
| 274 } | |
| 275 security_descriptor.SetOwner(Sids::Admins()); | |
| 276 security_descriptor.SetGroup(Sids::Admins()); | |
| 277 CDacl dacl; | |
| 278 dacl.AddAllowedAce(Sids::System(), COM_RIGHTS_EXECUTE); | |
| 279 dacl.AddAllowedAce(Sids::Admins(), COM_RIGHTS_EXECUTE); | |
| 280 dacl.AddAllowedAce(Sids::AuthenticatedUser(), COM_RIGHTS_EXECUTE); | |
| 281 | |
| 282 security_descriptor.SetDacl(dacl); | |
| 283 security_descriptor.MakeAbsolute(); | |
| 284 | |
| 285 SECURITY_DESCRIPTOR* sd = const_cast<SECURITY_DESCRIPTOR*>( | |
| 286 security_descriptor.GetPSECURITY_DESCRIPTOR()); | |
| 287 | |
| 288 return ::CoInitializeSecurity( | |
| 289 sd, | |
| 290 -1, | |
| 291 NULL, // Let COM choose what authentication services to register. | |
| 292 NULL, | |
| 293 RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Data integrity and encryption. | |
| 294 RPC_C_IMP_LEVEL_IDENTIFY, // Only allow a server to identify. | |
| 295 NULL, | |
| 296 EOAC_DYNAMIC_CLOAKING | EOAC_NO_CUSTOM_MARSHAL, | |
| 297 NULL); | |
| 298 } | |
| 299 | |
| 300 // This is only used for legacy handoff support. | |
| 301 CString GetProductName(const CString& app_guid) { | |
| 302 const TCHAR* product_name = NULL; | |
| 303 const TCHAR gears_guid[] = _T("{283EAF47-8817-4c2b-A801-AD1FADFB7BAA}"); | |
| 304 const TCHAR google_talk_plugin[] = | |
| 305 _T("{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}"); | |
| 306 const TCHAR youtube_uploader_guid[] = | |
| 307 _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}"); | |
| 308 | |
| 309 if (app_guid.CompareNoCase(gears_guid) == 0) { | |
| 310 product_name = _T("Gears"); | |
| 311 } else if (app_guid.CompareNoCase(google_talk_plugin) == 0) { | |
| 312 product_name = _T("Google Talk Plugin"); | |
| 313 } else if (app_guid.CompareNoCase(youtube_uploader_guid) == 0) { | |
| 314 product_name = _T("YouTube Uploader"); | |
| 315 } else { | |
| 316 product_name = _T("Google App"); | |
| 317 } | |
| 318 return product_name; | |
| 319 } | |
| 320 | |
| 321 HRESULT RedirectHKCR(bool is_machine) { | |
| 322 RegKey classes_key; | |
| 323 HRESULT hr = classes_key.Open(is_machine ? | |
| 324 HKEY_LOCAL_MACHINE : | |
| 325 HKEY_CURRENT_USER, | |
| 326 _T("Software\\Classes"), | |
| 327 KEY_ALL_ACCESS); | |
| 328 if (FAILED(hr)) { | |
| 329 ASSERT(FALSE, (_T("RedirectHKCR - key.Open(%d) fail %d"), is_machine, hr)); | |
| 330 return hr; | |
| 331 } | |
| 332 | |
| 333 LONG result = ::RegOverridePredefKey(HKEY_CLASSES_ROOT, classes_key.Key()); | |
| 334 if (result != ERROR_SUCCESS) { | |
| 335 ASSERT(false, (_T("RedirectHKCR - RegOverridePredefKey fail %d"), result)); | |
| 336 return HRESULT_FROM_WIN32(result); | |
| 337 } | |
| 338 | |
| 339 return S_OK; | |
| 340 } | |
| 341 | |
| 342 HRESULT RemoveRedirectHKCR() { | |
| 343 LONG result = ::RegOverridePredefKey(HKEY_CLASSES_ROOT, NULL); | |
| 344 if (result != ERROR_SUCCESS) { | |
| 345 ASSERT(FALSE, (_T("RemoveRedirectHKCR - RegOverridePredefKey %d"), result)); | |
| 346 return HRESULT_FROM_WIN32(result); | |
| 347 } | |
| 348 | |
| 349 return S_OK; | |
| 350 } | |
| 351 | |
| 352 HRESULT RegisterTypeLib(bool is_admin, | |
| 353 const CComBSTR& path, | |
| 354 ITypeLib* type_lib) { | |
| 355 // Typelib registration. | |
| 356 CORE_LOG(L3, (_T("[Registering TypeLib]"))); | |
| 357 HRESULT hr = S_OK; | |
| 358 if (!is_admin && | |
| 359 SUCCEEDED(goopdate_utils::RegisterTypeLibForUser(type_lib, path, NULL))) { | |
| 360 return S_OK; | |
| 361 } | |
| 362 | |
| 363 // For Admin cases, we use ::RegisterTypeLib(). | |
| 364 // For platforms where ::RegisterTypeLibForUser is not available, we register | |
| 365 // with ::RegisterTypeLib, and rely on HKCR=>HKCU redirection. | |
| 366 hr = ::RegisterTypeLib(type_lib, path, NULL); | |
| 367 ASSERT(SUCCEEDED(hr), (_T("[TypeLib registration failed][0x%08x]"), hr)); | |
| 368 return hr; | |
| 369 } | |
| 370 | |
| 371 HRESULT UnRegisterTypeLib(bool is_admin, const CComBSTR&, ITypeLib* type_lib) { | |
| 372 // Typelib unregistration. | |
| 373 CORE_LOG(L3, (_T("[Unregistering Typelib]"))); | |
| 374 TLIBATTR* tlib_attr = NULL; | |
| 375 HRESULT hr = type_lib->GetLibAttr(&tlib_attr); | |
| 376 ASSERT(SUCCEEDED(hr), (_T("[GetLibAttr failed][0x%08x]"), hr)); | |
| 377 if (FAILED(hr)) { | |
| 378 return hr; | |
| 379 } | |
| 380 ON_SCOPE_EXIT_OBJ(*type_lib, &ITypeLib::ReleaseTLibAttr, tlib_attr); | |
| 381 | |
| 382 if (!is_admin && | |
| 383 SUCCEEDED(goopdate_utils::UnRegisterTypeLibForUser( | |
| 384 tlib_attr->guid, | |
| 385 tlib_attr->wMajorVerNum, | |
| 386 tlib_attr->wMinorVerNum, | |
| 387 tlib_attr->lcid, | |
| 388 tlib_attr->syskind))) { | |
| 389 return S_OK; | |
| 390 } | |
| 391 | |
| 392 // For Admin cases, we use ::UnRegisterTypeLib(). | |
| 393 // For platforms where ::UnRegisterTypeLibForUser is not available, we | |
| 394 // unregister with ::UnRegisterTypeLib, and rely on HKCR=>HKCU redirection. | |
| 395 hr = ::UnRegisterTypeLib(tlib_attr->guid, | |
| 396 tlib_attr->wMajorVerNum, | |
| 397 tlib_attr->wMinorVerNum, | |
| 398 tlib_attr->lcid, | |
| 399 tlib_attr->syskind); | |
| 400 | |
| 401 // We assert before the check for TYPE_E_REGISTRYACCESS below because we want | |
| 402 // to catch the case where we're trying to unregister more than once because | |
| 403 // that would be a bug. | |
| 404 ASSERT(SUCCEEDED(hr), | |
| 405 (_T("[UnRegisterTypeLib failed. ") | |
| 406 _T("This is likely a multiple unregister bug.][0x%08x]"), hr)); | |
| 407 | |
| 408 // If you try to unregister a type library that's already unregistered, | |
| 409 // it will return with this failure, which is OK. | |
| 410 if (hr == TYPE_E_REGISTRYACCESS) { | |
| 411 hr = S_OK; | |
| 412 } | |
| 413 | |
| 414 return hr; | |
| 415 } | |
| 416 | |
| 417 HRESULT RegisterOrUnregisterModule(bool is_machine, | |
| 418 bool register_server, | |
| 419 RegisterOrUnregisterFunction registrar, | |
| 420 void* data) { | |
| 421 ASSERT1(registrar); | |
| 422 | |
| 423 // ATL by default registers the control to HKCR and we want to register | |
| 424 // either in HKLM, or in HKCU, depending on whether we are laying down | |
| 425 // the system googleupdate, or the user googleupdate. | |
| 426 // We solve this for the user goopdate case by: | |
| 427 // * Having the RGS file take a HKROOT parameter that translates to either | |
| 428 // HKLM or HKCU. | |
| 429 // * Redirecting HKCR to HKCU\software\classes, for a user installation, to | |
| 430 // cover Proxy registration. | |
| 431 // For the machine case, we still redirect HKCR to HKLM\\Software\\Classes, | |
| 432 // to ensure that Proxy registration happens in HKLM. | |
| 433 HRESULT hr = RedirectHKCR(is_machine); | |
| 434 ASSERT1(SUCCEEDED(hr)); | |
| 435 if (FAILED(hr)) { | |
| 436 return hr; | |
| 437 } | |
| 438 // We need to stop redirecting at the end of this function. | |
| 439 ON_SCOPE_EXIT(RemoveRedirectHKCR); | |
| 440 | |
| 441 hr = (*registrar)(data, register_server); | |
| 442 if (FAILED(hr)) { | |
| 443 CORE_LOG(LW, (_T("[RegisterOrUnregisterModule failed][%d][0x%08x]"), | |
| 444 register_server, hr)); | |
| 445 ASSERT1(!register_server); | |
| 446 } | |
| 447 | |
| 448 return hr; | |
| 449 } | |
| 450 | |
| 451 HRESULT RegisterOrUnregisterModuleWithTypelib( | |
| 452 bool is_machine, | |
| 453 bool register_server, | |
| 454 RegisterOrUnregisterFunction registrar, | |
| 455 void* data) { | |
| 456 ASSERT1(registrar); | |
| 457 | |
| 458 // By default, ATL registers the control to HKCR and we want to register | |
| 459 // either in HKLM, or in HKCU, depending on whether we are laying down | |
| 460 // the machine googleupdate, or the user googleupdate. | |
| 461 // We solve this for the user goopdate case by: | |
| 462 // * Having the RGS file take a HKROOT parameter that translates to either | |
| 463 // HKLM or HKCU. | |
| 464 // * Redirecting HKCR to HKCU\software\classes, for a user installation, to | |
| 465 // cover AppId and TypeLib registration | |
| 466 // * All the above makes ATL work correctly for 2K/XP. However on Win2K3 | |
| 467 // and Vista, redirection does not work by itself, because in these | |
| 468 // platforms, RegisterTypeLib writes explicitly to HKLM\Software\Classes. | |
| 469 // We need to specifically call the new RegisterTypeLibForUser() API. | |
| 470 // So, we do that as well. | |
| 471 // For the machine case, we still redirect HKCR to HKLM\\Software\\Classes, | |
| 472 // because otherwise RegisterTypeLib ends up overwriting HKCU if the key | |
| 473 // already exists in HKCU. | |
| 474 HRESULT hr = RedirectHKCR(is_machine); | |
| 475 ASSERT1(SUCCEEDED(hr)); | |
| 476 if (FAILED(hr)) { | |
| 477 return hr; | |
| 478 } | |
| 479 // We need to stop redirecting at the end of this function. | |
| 480 ON_SCOPE_EXIT(RemoveRedirectHKCR); | |
| 481 | |
| 482 // load the type library. | |
| 483 CComPtr<ITypeLib> type_lib; | |
| 484 CComBSTR path; | |
| 485 hr = ::AtlLoadTypeLib(_AtlBaseModule.GetModuleInstance(), NULL, &path, | |
| 486 &type_lib); | |
| 487 if (FAILED(hr)) { | |
| 488 ASSERT(false, (_T("[AtlLoadTypeLib failed][0x%08x]"), hr)); | |
| 489 return hr; | |
| 490 } | |
| 491 | |
| 492 if (register_server) { | |
| 493 hr = (*registrar)(data, register_server); | |
| 494 if (FAILED(hr)) { | |
| 495 ASSERT(false, (_T("[Module registration failed][0x%08x]"), hr)); | |
| 496 return hr; | |
| 497 } | |
| 498 | |
| 499 return RegisterTypeLib(is_machine, path, type_lib); | |
| 500 } else { | |
| 501 hr = UnRegisterTypeLib(is_machine, path, type_lib); | |
| 502 if (FAILED(hr)) { | |
| 503 ASSERT(false, (_T("[UnRegisterTypeLib failed][0x%08x]"), hr)); | |
| 504 return hr; | |
| 505 } | |
| 506 | |
| 507 return (*registrar)(data, register_server); | |
| 508 } | |
| 509 } | |
| 510 | |
| 511 HRESULT RegisterTypeLibForUser(ITypeLib* lib, | |
| 512 OLECHAR* path, | |
| 513 OLECHAR* help_dir) { | |
| 514 CORE_LOG(L3, (_T("[RegisterTypeLibForUser]"))); | |
| 515 ASSERT1(lib); | |
| 516 ASSERT1(path); | |
| 517 // help_dir can be NULL. | |
| 518 | |
| 519 const TCHAR* library_name = _T("oleaut32.dll"); | |
| 520 scoped_library module(static_cast<HINSTANCE>(::LoadLibrary(library_name))); | |
| 521 if (!module) { | |
| 522 HRESULT hr = HRESULTFromLastError(); | |
| 523 CORE_LOG(LEVEL_ERROR, | |
| 524 (_T("[LoadLibrary failed][%s][0x%08x]"), library_name, hr)); | |
| 525 return hr; | |
| 526 } | |
| 527 | |
| 528 // RegisterTypeLibForUser function from oleaut32.dll. | |
| 529 typedef HRESULT(__stdcall *PF)(ITypeLib*, OLECHAR*, OLECHAR*); | |
| 530 | |
| 531 const char* function_name = "RegisterTypeLibForUser"; | |
| 532 PF fp = reinterpret_cast<PF>(::GetProcAddress(get(module), function_name)); | |
| 533 if (!fp) { | |
| 534 HRESULT hr = HRESULTFromLastError(); | |
| 535 CORE_LOG(LEVEL_ERROR, | |
| 536 (_T("[GetProcAddress failed][%s][0x%08x]"), | |
| 537 function_name, library_name, hr)); | |
| 538 return hr; | |
| 539 } | |
| 540 | |
| 541 CORE_LOG(L3, (_T("[Calling RegisterTypelibForUser in oleaut]"))); | |
| 542 HRESULT hr = fp(lib, path, help_dir); | |
| 543 if (FAILED(hr)) { | |
| 544 CORE_LOG(LEVEL_ERROR, (_T("[regtypelib_for_user failed][0x%08x]"), hr)); | |
| 545 return hr; | |
| 546 } | |
| 547 | |
| 548 return S_OK; | |
| 549 } | |
| 550 | |
| 551 HRESULT UnRegisterTypeLibForUser(REFGUID lib_id, | |
| 552 WORD major_ver_num, | |
| 553 WORD minor_ver_num, | |
| 554 LCID lcid, | |
| 555 SYSKIND syskind) { | |
| 556 CORE_LOG(L3, (_T("[UnRegisterTypeLibForUser]"))); | |
| 557 | |
| 558 const TCHAR* library_name = _T("oleaut32.dll"); | |
| 559 scoped_library module(static_cast<HINSTANCE>(::LoadLibrary(library_name))); | |
| 560 if (!module) { | |
| 561 HRESULT hr = HRESULTFromLastError(); | |
| 562 CORE_LOG(LEVEL_ERROR, | |
| 563 (_T("[LoadLibrary failed][%s][0x%08x]"), library_name, hr)); | |
| 564 return hr; | |
| 565 } | |
| 566 | |
| 567 // UnRegisterTypeLibForUser function from oleaut32.dll. | |
| 568 typedef HRESULT (__stdcall *PF)(REFGUID, WORD, WORD, LCID, SYSKIND); | |
| 569 | |
| 570 const char* function_name = "UnRegisterTypeLibForUser"; | |
| 571 PF fp = reinterpret_cast<PF>(::GetProcAddress(get(module), function_name)); | |
| 572 if (!fp) { | |
| 573 HRESULT hr = HRESULTFromLastError(); | |
| 574 CORE_LOG(LEVEL_ERROR, | |
| 575 (_T("[GetProcAddress failed][%s][0x%08x]"), | |
| 576 function_name, library_name, hr)); | |
| 577 return hr; | |
| 578 } | |
| 579 | |
| 580 CORE_LOG(L3, (_T("[Calling UnRegisterTypeLibForUser in oleaut]"))); | |
| 581 HRESULT hr = fp(lib_id, major_ver_num, minor_ver_num, lcid, syskind); | |
| 582 if (FAILED(hr)) { | |
| 583 CORE_LOG(LEVEL_ERROR, (_T("[unregtypelib_for_user failed][0x%08x]"), hr)); | |
| 584 return hr; | |
| 585 } | |
| 586 | |
| 587 return S_OK; | |
| 588 } | |
| 589 | |
| 590 // TODO(omaha): This method's name is much more specific than what it does. Can | |
| 591 // we just copy the code to scheduled task and service code and eliminate it? | |
| 592 // Reads the current value under {HKLM|HKCU}\Google\Update\value_name. Returns | |
| 593 // default_val if value_name does not exist. | |
| 594 CString GetCurrentVersionedName(bool is_machine, | |
| 595 const TCHAR* value_name, | |
| 596 const TCHAR* default_val) { | |
| 597 CORE_LOG(L3, (_T("[ConfigManager::GetCurrentVersionedName]"))); | |
| 598 ASSERT1(value_name && *value_name); | |
| 599 ASSERT1(default_val && *default_val); | |
| 600 | |
| 601 const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE; | |
| 602 CString name; | |
| 603 HRESULT hr(RegKey::GetValue(key_name, value_name, &name)); | |
| 604 if (FAILED(hr)) { | |
| 605 CORE_LOG(L4, (_T("[GetValue failed][%s][0x%x][Using default name][%s]"), | |
| 606 value_name, hr, default_val)); | |
| 607 name = default_val; | |
| 608 } | |
| 609 | |
| 610 CORE_LOG(L3, (_T("[Versioned Name][%s]"), name)); | |
| 611 return name; | |
| 612 } | |
| 613 | |
| 614 // Creates a unique name of the form "{prefix}1c9b3d6baf90df3" and stores it in | |
| 615 // the registry under HKLM/HKCU\Google\Update\value_name. Subsequent | |
| 616 // invocations of GetCurrentTaskName() will return this new value. | |
| 617 HRESULT CreateAndSetVersionedNameInRegistry(bool is_machine, | |
| 618 const TCHAR* prefix, | |
| 619 const TCHAR* value_name) { | |
| 620 ASSERT1(prefix && *prefix); | |
| 621 ASSERT1(value_name && *value_name); | |
| 622 | |
| 623 // TODO(omaha): Move from service_utils.h since it is used for other purposes. | |
| 624 CString name(ServiceInstall::GenerateServiceName(prefix)); | |
| 625 CORE_LOG(L3, (_T("Versioned name[%s][%s][%s]"), prefix, value_name, name)); | |
| 626 | |
| 627 const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE; | |
| 628 return RegKey::SetValue(key_name, value_name, name); | |
| 629 } | |
| 630 | |
| 631 HRESULT TerminateAllBrowsers( | |
| 632 BrowserType type, | |
| 633 TerminateBrowserResult* browser_res, | |
| 634 TerminateBrowserResult* default_res) { | |
| 635 UTIL_LOG(L3, (_T("[TerminateAllBrowsers][%d]"), type)); | |
| 636 ASSERT1(default_res); | |
| 637 ASSERT1(browser_res); | |
| 638 | |
| 639 if (type == BROWSER_UNKNOWN || | |
| 640 type == BROWSER_DEFAULT || | |
| 641 type >= BROWSER_MAX) { | |
| 642 ASSERT1(false); | |
| 643 return E_INVALIDARG; | |
| 644 } | |
| 645 | |
| 646 const BrowserType kFirstBrowser = BROWSER_IE; | |
| 647 const int kNumSupportedBrowsers = BROWSER_MAX - kFirstBrowser; | |
| 648 | |
| 649 BrowserType default_type = BROWSER_UNKNOWN; | |
| 650 HRESULT hr = GetDefaultBrowserType(&default_type); | |
| 651 if (FAILED(hr)) { | |
| 652 UTIL_LOG(LW, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr)); | |
| 653 return hr; | |
| 654 } | |
| 655 | |
| 656 TerminateBrowserResult terminate_results[kNumSupportedBrowsers]; | |
| 657 | |
| 658 for (int browser = 0; browser < kNumSupportedBrowsers; ++browser) { | |
| 659 const BrowserType browser_type = | |
| 660 static_cast<BrowserType>(kFirstBrowser + browser); | |
| 661 hr = TerminateBrowserProcess(browser_type, | |
| 662 CString(), | |
| 663 0, | |
| 664 &terminate_results[browser].found); | |
| 665 if (FAILED(hr)) { | |
| 666 UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][%u][0x%08x]"), | |
| 667 browser_type, hr)); | |
| 668 } | |
| 669 } | |
| 670 | |
| 671 // Now wait for the all browser instances to die. | |
| 672 // TODO(omaha): Wait for all processes at once rather than waiting for | |
| 673 // (kTerminateBrowserTimeoutMs * # supported browsers) ms. | |
| 674 for (int browser = 0; browser < kNumSupportedBrowsers; ++browser) { | |
| 675 const BrowserType browser_type = | |
| 676 static_cast<BrowserType>(kFirstBrowser + browser); | |
| 677 hr = WaitForBrowserToDie(browser_type, | |
| 678 CString(), | |
| 679 kTerminateBrowserTimeoutMs); | |
| 680 if (FAILED(hr)) { | |
| 681 UTIL_LOG(LW, (_T("[WaitForBrowserToDie failed][%u][0x%08x]"), | |
| 682 browser_type, hr)); | |
| 683 } else { | |
| 684 terminate_results[browser].could_terminate = true; | |
| 685 } | |
| 686 } | |
| 687 | |
| 688 *browser_res = terminate_results[type - kFirstBrowser]; | |
| 689 *default_res = terminate_results[default_type - kFirstBrowser]; | |
| 690 | |
| 691 return S_OK; | |
| 692 } | |
| 693 | |
| 694 // default_type can be BROWSER_UNKNOWN. | |
| 695 // If browsers that must be closed could not be terminated, false is returned. | |
| 696 // This method and TerminateBrowserProcesses assume the user did not shutdown | |
| 697 // the specified browser. They restart and shutdown, respectively, the default | |
| 698 // browser when the specified browser is not found. The reason for this may have | |
| 699 // been that the the specified (stamped) browser could be in a bad state on the | |
| 700 // machine and trying to start it would fail. | |
| 701 // This may also be required to support hosted cases (i.e. AOL and Maxthon). | |
| 702 // TODO(omaha): If we assume the stamped browser is okay, check whether the | |
| 703 // specified browser is installed rather than relying on whether the browser was | |
| 704 // running. It is perfectly valid for the browser to not be running. | |
| 705 // TODO(omaha): Why not try the default browser if browsers that require | |
| 706 // shutdown failed to terminate. | |
| 707 bool GetBrowserToRestart(BrowserType type, | |
| 708 BrowserType default_type, | |
| 709 const TerminateBrowserResult& res, | |
| 710 const TerminateBrowserResult& def_res, | |
| 711 BrowserType* browser_type) { | |
| 712 ASSERT1(browser_type); | |
| 713 ASSERT1(type != BROWSER_UNKNOWN && | |
| 714 type != BROWSER_DEFAULT && | |
| 715 type < BROWSER_MAX); | |
| 716 ASSERT1(default_type != BROWSER_DEFAULT && default_type < BROWSER_MAX); | |
| 717 UTIL_LOG(L3, (_T("[GetBrowserToRestart][%d]"), type)); | |
| 718 | |
| 719 *browser_type = BROWSER_UNKNOWN; | |
| 720 | |
| 721 if (res.found) { | |
| 722 switch (type) { | |
| 723 case BROWSER_IE: | |
| 724 *browser_type = BROWSER_IE; | |
| 725 return true; | |
| 726 case BROWSER_FIREFOX: // Only one process. | |
| 727 case BROWSER_CHROME: // One process per plug-in, even for upgrades. | |
| 728 if (res.could_terminate) { | |
| 729 *browser_type = type; | |
| 730 return true; | |
| 731 } | |
| 732 return false; | |
| 733 case BROWSER_UNKNOWN: | |
| 734 case BROWSER_DEFAULT: | |
| 735 case BROWSER_MAX: | |
| 736 default: | |
| 737 break; | |
| 738 } | |
| 739 } | |
| 740 | |
| 741 // We did not find the browser that we wanted to restart. Hence we need to | |
| 742 // determine if we could shutdown the default browser. | |
| 743 switch (default_type) { | |
| 744 case BROWSER_IE: | |
| 745 *browser_type = BROWSER_IE; | |
| 746 return true; | |
| 747 case BROWSER_FIREFOX: | |
| 748 case BROWSER_CHROME: | |
| 749 if (!def_res.found || def_res.found && def_res.could_terminate) { | |
| 750 *browser_type = default_type; | |
| 751 return true; | |
| 752 } | |
| 753 break; | |
| 754 case BROWSER_UNKNOWN: | |
| 755 case BROWSER_DEFAULT: | |
| 756 case BROWSER_MAX: | |
| 757 default: | |
| 758 break; | |
| 759 } | |
| 760 | |
| 761 return false; | |
| 762 } | |
| 763 | |
| 764 // See the comments about the default browser above GetBrowserToRestart. | |
| 765 HRESULT TerminateBrowserProcesses(BrowserType type, | |
| 766 TerminateBrowserResult* browser_res, | |
| 767 TerminateBrowserResult* default_res) { | |
| 768 UTIL_LOG(L3, (_T("[TerminateBrowserProcesses][%d]"), type)); | |
| 769 ASSERT1(browser_res); | |
| 770 ASSERT1(default_res); | |
| 771 | |
| 772 browser_res->could_terminate = false; | |
| 773 default_res->could_terminate = false; | |
| 774 | |
| 775 if (type == BROWSER_UNKNOWN || | |
| 776 type == BROWSER_DEFAULT || | |
| 777 type >= BROWSER_MAX) { | |
| 778 ASSERT1(false); | |
| 779 return E_UNEXPECTED; | |
| 780 } | |
| 781 | |
| 782 CString sid; | |
| 783 HRESULT hr = user_info::GetProcessUser(NULL, NULL, &sid); | |
| 784 if (FAILED(hr)) { | |
| 785 UTIL_LOG(LE, (_T("[GetProcessUser failed][0x%08x]"), hr)); | |
| 786 return hr; | |
| 787 } | |
| 788 | |
| 789 hr = TerminateBrowserProcess(type, | |
| 790 sid, | |
| 791 kTerminateBrowserTimeoutMs, | |
| 792 &browser_res->found); | |
| 793 if (FAILED(hr)) { | |
| 794 UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][0x%08x]"), hr)); | |
| 795 } else { | |
| 796 browser_res->could_terminate = true; | |
| 797 } | |
| 798 | |
| 799 // Since no instances of the browser type exist, we try to find and kill | |
| 800 // all instances of the default browser. | |
| 801 if (!browser_res->found) { | |
| 802 // We dont want to try and terminate the default browser, if it is the | |
| 803 // same as the browser that we tried above. | |
| 804 | |
| 805 BrowserType default_type = BROWSER_UNKNOWN; | |
| 806 hr = GetDefaultBrowserType(&default_type); | |
| 807 if (FAILED(hr)) { | |
| 808 UTIL_LOG(LW, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr)); | |
| 809 } | |
| 810 | |
| 811 UTIL_LOG(L3, (_T("[Trying to kill the default browser %d]"), default_type)); | |
| 812 if (default_type != type) { | |
| 813 hr = TerminateBrowserProcess(BROWSER_DEFAULT, | |
| 814 sid, | |
| 815 kTerminateBrowserTimeoutMs, | |
| 816 &default_res->found); | |
| 817 if (FAILED(hr)) { | |
| 818 UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][0x%08x]"), hr)); | |
| 819 } else { | |
| 820 default_res->could_terminate = true; | |
| 821 } | |
| 822 } | |
| 823 } | |
| 824 | |
| 825 return hr; | |
| 826 } | |
| 827 | |
| 828 HRESULT GetBrowserImagePathFromProcess(BrowserType type, | |
| 829 uint32 explorer_pid, | |
| 830 CString* path) { | |
| 831 ASSERT1(path); | |
| 832 | |
| 833 if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) { | |
| 834 ASSERT1(false); | |
| 835 return E_UNEXPECTED; | |
| 836 } | |
| 837 | |
| 838 if (type == BROWSER_DEFAULT) { | |
| 839 return GetDefaultBrowserPath(path); | |
| 840 } | |
| 841 | |
| 842 CString user_sid; | |
| 843 HRESULT hr = Process::GetProcessOwner(explorer_pid, &user_sid); | |
| 844 if (FAILED(hr)) { | |
| 845 UTIL_LOG(LEVEL_WARNING, (_T("[GetProcessOwner failed.][0x%08x]"), hr)); | |
| 846 return hr; | |
| 847 } | |
| 848 | |
| 849 CString browser_name; | |
| 850 hr = BrowserTypeToProcessName(type, &browser_name); | |
| 851 if (FAILED(hr)) { | |
| 852 UTIL_LOG(LW, (_T("[BrowserTypeToProcessName failed.][0x%08x]"), hr)); | |
| 853 return hr; | |
| 854 } | |
| 855 | |
| 856 hr = Process::GetImagePath(browser_name, user_sid, path); | |
| 857 if (FAILED(hr)) { | |
| 858 UTIL_LOG(LW, (_T("[GetImagePath failed.][0x%08x]"), hr)); | |
| 859 return hr; | |
| 860 } | |
| 861 | |
| 862 return S_OK; | |
| 863 } | |
| 864 | |
| 865 HRESULT ConvertStringToBrowserType(const CString& text, BrowserType* type) { | |
| 866 ASSERT1(type != NULL); | |
| 867 | |
| 868 if (text.GetLength() != 1) { | |
| 869 return GOOPDATEUTILS_E_BROWSERTYPE; | |
| 870 } | |
| 871 | |
| 872 int browser_type = 0; | |
| 873 if (!String_StringToDecimalIntChecked(text, &browser_type)) { | |
| 874 return GOOPDATEUTILS_E_BROWSERTYPE; | |
| 875 } | |
| 876 | |
| 877 if (browser_type >= BROWSER_MAX) { | |
| 878 return GOOPDATEUTILS_E_BROWSERTYPE; | |
| 879 } | |
| 880 | |
| 881 *type = static_cast<BrowserType>(browser_type); | |
| 882 return S_OK; | |
| 883 } | |
| 884 | |
| 885 CString ConvertBrowserTypeToString(BrowserType type) { | |
| 886 CString text = itostr(static_cast<int>(type)); | |
| 887 ASSERT1(!text.IsEmpty()); | |
| 888 return text; | |
| 889 } | |
| 890 | |
| 891 HRESULT GetOSInfo(CString* os_version, CString* service_pack) { | |
| 892 ASSERT1(os_version); | |
| 893 ASSERT1(service_pack); | |
| 894 | |
| 895 OSVERSIONINFO os_version_info = { 0 }; | |
| 896 os_version_info.dwOSVersionInfoSize = sizeof(os_version_info); | |
| 897 if (!::GetVersionEx(&os_version_info)) { | |
| 898 HRESULT hr = HRESULTFromLastError(); | |
| 899 UTIL_LOG(LW, (_T("[GetVersionEx failed][0x%08x]"), hr)); | |
| 900 return hr; | |
| 901 } | |
| 902 | |
| 903 os_version->Format(_T("%d.%d"), | |
| 904 os_version_info.dwMajorVersion, | |
| 905 os_version_info.dwMinorVersion); | |
| 906 *service_pack = os_version_info.szCSDVersion; | |
| 907 return S_OK; | |
| 908 } | |
| 909 | |
| 910 CPath BuildInstallDirectory(bool is_machine, const CString& version) { | |
| 911 ConfigManager& cm = *ConfigManager::Instance(); | |
| 912 CPath install_dir(is_machine ? cm.GetMachineGoopdateInstallDir() : | |
| 913 cm.GetUserGoopdateInstallDir()); | |
| 914 VERIFY1(install_dir.Append(version)); | |
| 915 | |
| 916 return install_dir; | |
| 917 } | |
| 918 | |
| 919 // This method does a very specific job of searching for install workers, | |
| 920 // for the user and machine omaha. It also includes the on-demand updates COM | |
| 921 // server, because we treat it similar to interactive installs, and selfupdate. | |
| 922 // | |
| 923 // In machine case we search in all the accounts since the install worker can be | |
| 924 // running in any admin account and the machine update worker runs as SYSTEM. | |
| 925 // In the user case, we only search the user's account. | |
| 926 // In both cases, the Needsadmin command line parameter is checked for | |
| 927 // true/false in the machine/user case, respectively. | |
| 928 // | |
| 929 // Only adds processes to the input vector; does not clear it. | |
| 930 // | |
| 931 // TODO(omaha): For now we search for the needs_admin=true in the command | |
| 932 // line to determine a machine install. Another option of identifying omaha's | |
| 933 // is to use the presence of a named mutex. So the user omaha will create | |
| 934 // Global\<sid>\Mutex and the machine will create Global\Mutex, in here then | |
| 935 // we can test for the presence of the name to decide if an interactive | |
| 936 // omaha is running. | |
| 937 // TODO(omaha): Consider further filtering the processes based on whether | |
| 938 // the owner is elevated in case of machine omaha. | |
| 939 // Looks for the /ig command line used in Omaha 2. | |
| 940 HRESULT GetInstallWorkerProcesses(bool is_machine, | |
| 941 std::vector<uint32>* processes) { | |
| 942 ASSERT1(processes); | |
| 943 | |
| 944 CString user_sid; | |
| 945 DWORD flags = EXCLUDE_CURRENT_PROCESS | | |
| 946 EXCLUDE_PARENT_PROCESS | | |
| 947 INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING; | |
| 948 | |
| 949 std::vector<CString> command_lines; | |
| 950 CString command_line_to_include; | |
| 951 command_line_to_include.Format(_T("/%s"), kCmdLineInstall); | |
| 952 command_lines.push_back(command_line_to_include); | |
| 953 command_line_to_include.Format(_T("/%s"), kCmdLineInstallElevated); | |
| 954 command_lines.push_back(command_line_to_include); | |
| 955 command_line_to_include.Format(_T("/%s"), kCmdLineAppHandoffInstall); | |
| 956 command_lines.push_back(command_line_to_include); | |
| 957 command_line_to_include.Format(_T("/%s"), kCmdLineUpdate); | |
| 958 command_lines.push_back(command_line_to_include); | |
| 959 command_line_to_include.Format(_T("/%s"), | |
| 960 kCmdLineLegacyFinishGoogleUpdateInstall); | |
| 961 command_lines.push_back(command_line_to_include); | |
| 962 command_lines.push_back(kCmdLineComServerDash); | |
| 963 | |
| 964 if (!is_machine) { | |
| 965 // Search only the same sid as the current user. | |
| 966 flags |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER; | |
| 967 | |
| 968 HRESULT hr = user_info::GetProcessUser(NULL, NULL, &user_sid); | |
| 969 if (FAILED(hr)) { | |
| 970 CORE_LOG(LE, (_T("[GetProcessUser failed][0x%08x]"), hr)); | |
| 971 return hr; | |
| 972 } | |
| 973 } | |
| 974 | |
| 975 std::vector<uint32> all_install_worker_processes; | |
| 976 HRESULT hr = Process::FindProcesses(flags, | |
| 977 kOmahaShellFileName, | |
| 978 true, | |
| 979 user_sid, | |
| 980 command_lines, | |
| 981 &all_install_worker_processes); | |
| 982 if (FAILED(hr)) { | |
| 983 CORE_LOG(LE, (_T("[FindProcesses failed][0x%08x]"), hr)); | |
| 984 return hr; | |
| 985 } | |
| 986 | |
| 987 CString official_path; | |
| 988 hr = GetFolderPath(is_machine ? CSIDL_PROGRAM_FILES : CSIDL_LOCAL_APPDATA, | |
| 989 &official_path); | |
| 990 ASSERT1(SUCCEEDED(hr)); | |
| 991 ASSERT1(!official_path.IsEmpty()); | |
| 992 | |
| 993 for (size_t i = 0; i < all_install_worker_processes.size(); ++i) { | |
| 994 CString cmd_line; | |
| 995 const uint32 process = all_install_worker_processes[i]; | |
| 996 if (SUCCEEDED(Process::GetCommandLine(process, &cmd_line))) { | |
| 997 cmd_line.MakeLower(); | |
| 998 // TODO(omaha): FindProcess method does not allow regex's to be specified | |
| 999 // along with the include command line. Change Process to allow this. | |
| 1000 if (cmd_line.Find(is_machine ? kNeedsAdminYes : kNeedsAdminNo) != -1) { | |
| 1001 CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line)); | |
| 1002 processes->push_back(process); | |
| 1003 } | |
| 1004 | |
| 1005 // A needsadmin=prefers instance could be installing either for machine or | |
| 1006 // for user. | |
| 1007 if (cmd_line.Find(kNeedsAdminPrefers) != -1) { | |
| 1008 CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line)); | |
| 1009 processes->push_back(process); | |
| 1010 } | |
| 1011 | |
| 1012 // The -Embedding does not have a needsAdmin. Decide whether to include it | |
| 1013 // if it matches the official path for the requested instance type. | |
| 1014 CString exe_path; | |
| 1015 if (cmd_line.Find(kCmdLineComServerDash) != -1 && | |
| 1016 SUCCEEDED(GetExePathFromCommandLine(cmd_line, &exe_path)) && | |
| 1017 String_StrNCmp(official_path, exe_path, official_path.GetLength(), | |
| 1018 true) == 0) { | |
| 1019 CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line)); | |
| 1020 processes->push_back(process); | |
| 1021 } | |
| 1022 } | |
| 1023 } | |
| 1024 | |
| 1025 return S_OK; | |
| 1026 } | |
| 1027 | |
| 1028 // The event name saved to the environment variable does not contain the | |
| 1029 // decoration added by GetNamedObjectAttributes. | |
| 1030 HRESULT CreateUniqueEventInEnvironment(const CString& var_name, | |
| 1031 bool is_machine, | |
| 1032 HANDLE* unique_event) { | |
| 1033 ASSERT1(unique_event); | |
| 1034 | |
| 1035 GUID event_guid = GUID_NULL; | |
| 1036 HRESULT hr = ::CoCreateGuid(&event_guid); | |
| 1037 if (FAILED(hr)) { | |
| 1038 CORE_LOG(LE, (_T("[::CoCreateGuid failed][0x%08x]"), hr)); | |
| 1039 return hr; | |
| 1040 } | |
| 1041 | |
| 1042 CString event_name(GuidToString(event_guid)); | |
| 1043 NamedObjectAttributes attr; | |
| 1044 GetNamedObjectAttributes(event_name, is_machine, &attr); | |
| 1045 | |
| 1046 hr = CreateEvent(&attr, unique_event); | |
| 1047 if (FAILED(hr)) { | |
| 1048 CORE_LOG(LW, (_T("[CreateEvent failed in CreateUniqueEventInEnvironment]"), | |
| 1049 _T("[%s][0x%08x]"), var_name, hr)); | |
| 1050 return hr; | |
| 1051 } | |
| 1052 | |
| 1053 CORE_LOG(L3, (_T("[created unique event][%s][%s]"), var_name, event_name)); | |
| 1054 | |
| 1055 if (!::SetEnvironmentVariable(var_name, event_name)) { | |
| 1056 DWORD error = ::GetLastError(); | |
| 1057 CORE_LOG(LE, (_T("[::SetEnvironmentVariable failed][%d]"), error)); | |
| 1058 return HRESULT_FROM_WIN32(error); | |
| 1059 } | |
| 1060 | |
| 1061 return S_OK; | |
| 1062 } | |
| 1063 | |
| 1064 HRESULT OpenUniqueEventFromEnvironment(const CString& var_name, | |
| 1065 bool is_machine, | |
| 1066 HANDLE* unique_event) { | |
| 1067 ASSERT1(unique_event); | |
| 1068 | |
| 1069 TCHAR event_name[MAX_PATH] = {0}; | |
| 1070 if (!::GetEnvironmentVariable(var_name, event_name, arraysize(event_name))) { | |
| 1071 DWORD error = ::GetLastError(); | |
| 1072 CORE_LOG(LW, (_T("[Failed to read environment variable][%s][%d]"), | |
| 1073 var_name, error)); | |
| 1074 return HRESULT_FROM_WIN32(error); | |
| 1075 } | |
| 1076 | |
| 1077 CORE_LOG(L3, (_T("[read unique event][%s][%s]"), var_name, event_name)); | |
| 1078 | |
| 1079 NamedObjectAttributes attr; | |
| 1080 GetNamedObjectAttributes(event_name, is_machine, &attr); | |
| 1081 *unique_event = ::OpenEvent(EVENT_ALL_ACCESS, false, attr.name); | |
| 1082 | |
| 1083 if (!*unique_event) { | |
| 1084 DWORD error = ::GetLastError(); | |
| 1085 CORE_LOG(LW, (_T("[::OpenEvent failed][%s][%d]"), attr.name, error)); | |
| 1086 return HRESULT_FROM_WIN32(error); | |
| 1087 } | |
| 1088 | |
| 1089 return S_OK; | |
| 1090 } | |
| 1091 | |
| 1092 // The caller is responsible for reseting the event and closing the handle. | |
| 1093 HRESULT CreateEvent(NamedObjectAttributes* event_attr, HANDLE* event_handle) { | |
| 1094 ASSERT1(event_handle); | |
| 1095 ASSERT1(event_attr); | |
| 1096 ASSERT1(!event_attr->name.IsEmpty()); | |
| 1097 *event_handle = ::CreateEvent(&event_attr->sa, | |
| 1098 true, // manual reset | |
| 1099 false, // not signaled | |
| 1100 event_attr->name); | |
| 1101 | |
| 1102 if (!*event_handle) { | |
| 1103 DWORD error = ::GetLastError(); | |
| 1104 CORE_LOG(LEVEL_ERROR, (_T("[::CreateEvent failed][%d]"), error)); | |
| 1105 return HRESULT_FROM_WIN32(error); | |
| 1106 } | |
| 1107 | |
| 1108 return S_OK; | |
| 1109 } | |
| 1110 | |
| 1111 bool IsTestSource() { | |
| 1112 return !ConfigManager::Instance()->GetTestSource().IsEmpty(); | |
| 1113 } | |
| 1114 | |
| 1115 HRESULT ReadNameValuePairsFromFile(const CString& file_path, | |
| 1116 const CString& group_name, | |
| 1117 std::map<CString, CString>* pairs) { | |
| 1118 ASSERT1(pairs); | |
| 1119 | |
| 1120 if (!File::Exists(file_path)) { | |
| 1121 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | |
| 1122 } | |
| 1123 | |
| 1124 pairs->clear(); | |
| 1125 | |
| 1126 TCHAR str_buf[32768] = {0}; | |
| 1127 | |
| 1128 // Retrieve all key names in the section requested. | |
| 1129 DWORD buf_count = ::GetPrivateProfileString(group_name, | |
| 1130 NULL, | |
| 1131 NULL, | |
| 1132 str_buf, | |
| 1133 arraysize(str_buf), | |
| 1134 file_path); | |
| 1135 | |
| 1136 DWORD offset = 0; | |
| 1137 while (offset < buf_count) { | |
| 1138 TCHAR val_buf[1024] = {0}; | |
| 1139 CString current_key = &(str_buf[offset]); | |
| 1140 DWORD val_count = ::GetPrivateProfileString(group_name, | |
| 1141 current_key, | |
| 1142 NULL, | |
| 1143 val_buf, | |
| 1144 arraysize(val_buf), | |
| 1145 file_path); | |
| 1146 (*pairs)[current_key] = val_buf; | |
| 1147 offset += current_key.GetLength() + 1; | |
| 1148 } | |
| 1149 | |
| 1150 return S_OK; | |
| 1151 } | |
| 1152 | |
| 1153 HRESULT WriteNameValuePairsToFile(const CString& file_path, | |
| 1154 const CString& group_name, | |
| 1155 const std::map<CString, CString>& pairs) { | |
| 1156 std::map<CString, CString>::const_iterator it = pairs.begin(); | |
| 1157 for (; it != pairs.end(); ++it) { | |
| 1158 if (!::WritePrivateProfileString(group_name, | |
| 1159 it->first, | |
| 1160 it->second, | |
| 1161 file_path)) { | |
| 1162 return HRESULTFromLastError(); | |
| 1163 } | |
| 1164 } | |
| 1165 | |
| 1166 return S_OK; | |
| 1167 } | |
| 1168 | |
| 1169 bool IsAppInstallWorkerRunning(bool is_machine) { | |
| 1170 CORE_LOG(L3, (_T("[IsAppInstallWorkerRunning][%d]"), is_machine)); | |
| 1171 std::vector<uint32> processes; | |
| 1172 VERIFY1(SUCCEEDED(GetInstallWorkerProcesses(is_machine, &processes))); | |
| 1173 return !processes.empty(); | |
| 1174 } | |
| 1175 | |
| 1176 // Returns true if the version does not begin with "1.0." or "1.1.". | |
| 1177 bool IsGoogleUpdate2OrLater(const CString& version) { | |
| 1178 const ULONGLONG kFirstOmaha2Version = MAKEDLLVERULL(1, 2, 0, 0); | |
| 1179 ULONGLONG version_number = VersionFromString(version); | |
| 1180 ASSERT1(0 != version_number); | |
| 1181 | |
| 1182 if (kFirstOmaha2Version <= version_number) { | |
| 1183 return true; | |
| 1184 } | |
| 1185 | |
| 1186 return false; | |
| 1187 } | |
| 1188 | |
| 1189 HRESULT WriteInstallerDataToTempFile(const CString& installer_data, | |
| 1190 CString* installer_data_file_path) { | |
| 1191 ASSERT1(installer_data_file_path); | |
| 1192 | |
| 1193 // TODO(omaha): consider eliminating the special case and simply create an | |
| 1194 // empty file. | |
| 1195 CORE_LOG(L2, (_T("[WriteInstallerDataToTempFile][data=%s]"), installer_data)); | |
| 1196 if (installer_data.IsEmpty()) { | |
| 1197 return S_FALSE; | |
| 1198 } | |
| 1199 | |
| 1200 CString temp_file; | |
| 1201 if (!::GetTempFileName(app_util::GetTempDir(), | |
| 1202 _T("gui"), | |
| 1203 0, | |
| 1204 CStrBuf(temp_file, MAX_PATH))) { | |
| 1205 HRESULT hr = HRESULTFromLastError(); | |
| 1206 CORE_LOG(LE, (_T("[::GetTempFileName failed][0x08%x]"), hr)); | |
| 1207 return hr; | |
| 1208 } | |
| 1209 | |
| 1210 scoped_hfile file_handle(::CreateFile(temp_file, | |
| 1211 GENERIC_WRITE, | |
| 1212 FILE_SHARE_READ, | |
| 1213 NULL, | |
| 1214 CREATE_ALWAYS, | |
| 1215 FILE_ATTRIBUTE_NORMAL, | |
| 1216 NULL)); | |
| 1217 if (!file_handle) { | |
| 1218 HRESULT hr = HRESULTFromLastError(); | |
| 1219 CORE_LOG(LE, (_T("[::CreateFile failed][0x08%x]"), hr)); | |
| 1220 return hr; | |
| 1221 } | |
| 1222 | |
| 1223 CStringA installer_data_utf8_bom; | |
| 1224 SafeCStringAFormat(&installer_data_utf8_bom, "%c%c%c%s", | |
| 1225 0xEF, 0xBB, 0xBF, WideToUtf8(installer_data)); | |
| 1226 | |
| 1227 DWORD bytes_written = 0; | |
| 1228 if (!::WriteFile(get(file_handle), | |
| 1229 installer_data_utf8_bom, | |
| 1230 installer_data_utf8_bom.GetLength(), | |
| 1231 &bytes_written, | |
| 1232 NULL)) { | |
| 1233 HRESULT hr = HRESULTFromLastError(); | |
| 1234 CORE_LOG(LE, (_T("[::WriteFile failed][0x08%x]"), hr)); | |
| 1235 return hr; | |
| 1236 } | |
| 1237 | |
| 1238 *installer_data_file_path = temp_file; | |
| 1239 return S_OK; | |
| 1240 } | |
| 1241 | |
| 1242 // Returns true if the absolute difference between time moments is greater than | |
| 1243 // the interval between update checks. | |
| 1244 // Deals with clocks rolling backwards, in scenarios where the clock indicates | |
| 1245 // some time in the future, for example next year, last_checked_ is updated to | |
| 1246 // reflect that time, and then the clock is adjusted back to present. | |
| 1247 bool ShouldCheckForUpdates(bool is_machine) { | |
| 1248 ConfigManager* cm = ConfigManager::Instance(); | |
| 1249 bool is_period_overridden = false; | |
| 1250 const int update_interval = cm->GetLastCheckPeriodSec(&is_period_overridden); | |
| 1251 if (0 == update_interval) { | |
| 1252 ASSERT1(is_period_overridden); | |
| 1253 OPT_LOG(L1, (_T("[ShouldCheckForUpdates returned 0][checks disabled]"))); | |
| 1254 return false; | |
| 1255 } | |
| 1256 | |
| 1257 const int time_difference = cm->GetTimeSinceLastCheckedSec(is_machine); | |
| 1258 | |
| 1259 const bool result = time_difference >= update_interval ? true : false; | |
| 1260 CORE_LOG(L3, (_T("[ShouldCheckForUpdates returned %d][%u]"), | |
| 1261 result, is_period_overridden)); | |
| 1262 return result; | |
| 1263 } | |
| 1264 | |
| 1265 HRESULT UpdateLastChecked(bool is_machine) { | |
| 1266 // Set the last check value to the current value. | |
| 1267 DWORD now = Time64ToInt32(GetCurrent100NSTime()); | |
| 1268 CORE_LOG(L3, (_T("[UpdateLastChecked][now %d]"), now)); | |
| 1269 HRESULT hr = ConfigManager::Instance()->SetLastCheckedTime(is_machine, now); | |
| 1270 if (FAILED(hr)) { | |
| 1271 CORE_LOG(LE, (_T("[SetLastCheckedTime failed][0x%08x]"), hr)); | |
| 1272 return hr; | |
| 1273 } | |
| 1274 return S_OK; | |
| 1275 } | |
| 1276 | |
| 1277 HRESULT LaunchUninstallProcess(bool is_machine) { | |
| 1278 CORE_LOG(L2, (_T("[LaunchUninstallProcess]"))); | |
| 1279 CString exe_path = BuildGoogleUpdateExePath(is_machine); | |
| 1280 CommandLineBuilder builder(COMMANDLINE_MODE_UNINSTALL); | |
| 1281 CString cmd_line = builder.GetCommandLineArgs(); | |
| 1282 return System::StartProcessWithArgs(exe_path, cmd_line); | |
| 1283 } | |
| 1284 | |
| 1285 HANDLE GetImpersonationTokenForMachineProcess(bool is_machine) { | |
| 1286 if (!is_machine) { | |
| 1287 return NULL; | |
| 1288 } | |
| 1289 | |
| 1290 CAccessToken access_token; | |
| 1291 if (access_token.GetThreadToken(TOKEN_READ)) { | |
| 1292 return NULL; | |
| 1293 } | |
| 1294 | |
| 1295 bool is_local_system(false); | |
| 1296 VERIFY1(SUCCEEDED(IsSystemProcess(&is_local_system))); | |
| 1297 if (!is_local_system) { | |
| 1298 return NULL; | |
| 1299 } | |
| 1300 | |
| 1301 HANDLE handle = NULL; | |
| 1302 HRESULT hr = vista::GetLoggedOnUserToken(&handle); | |
| 1303 if (FAILED(hr)) { | |
| 1304 UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr)); | |
| 1305 return NULL; | |
| 1306 } | |
| 1307 | |
| 1308 return handle; | |
| 1309 } | |
| 1310 | |
| 1311 HRESULT EnableSEHOP(bool enable) { | |
| 1312 CORE_LOG(L3, (_T("[EnableSEHOP][%d]"), enable)); | |
| 1313 CString omaha_ifeo_key_path; | |
| 1314 omaha_ifeo_key_path.Format(_T("%s\\%s"), | |
| 1315 kRegKeyImageFileExecutionOptions, | |
| 1316 kOmahaShellFileName); | |
| 1317 return enable ? | |
| 1318 RegKey::SetValue(omaha_ifeo_key_path, kRegKeyDisableSEHOPValue, | |
| 1319 static_cast<DWORD>(0)) : | |
| 1320 RegKey::DeleteValue(omaha_ifeo_key_path, kRegKeyDisableSEHOPValue); | |
| 1321 } | |
| 1322 | |
| 1323 DEFINE_METRIC_count(opt_in_uid_generated); | |
| 1324 HRESULT CreateUserId(bool is_machine) { | |
| 1325 // Do not create user ID when doing OEM installation - to avoid a large | |
| 1326 // number of machines have the same ID. | |
| 1327 if (oem_install_utils::IsOemInstalling(is_machine)) { | |
| 1328 return E_FAIL; | |
| 1329 } | |
| 1330 | |
| 1331 GLock user_id_lock; | |
| 1332 NamedObjectAttributes lock_attr; | |
| 1333 GetNamedObjectAttributes(kOptUserIdLock, is_machine, &lock_attr); | |
| 1334 if (!user_id_lock.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa)) { | |
| 1335 return E_FAIL; | |
| 1336 } | |
| 1337 | |
| 1338 __mutexScope(user_id_lock); | |
| 1339 RegKey update_key; | |
| 1340 const ConfigManager& config_manager = *ConfigManager::Instance(); | |
| 1341 HRESULT hr = update_key.Create(config_manager.registry_update(is_machine)); | |
| 1342 if (FAILED(hr)) { | |
| 1343 return hr; | |
| 1344 } | |
| 1345 | |
| 1346 if (update_key.HasValue(kRegValueUserId)) { | |
| 1347 return S_OK; | |
| 1348 } | |
| 1349 | |
| 1350 CString user_id; | |
| 1351 hr = GetGuid(&user_id); | |
| 1352 if (FAILED(hr)) { | |
| 1353 return hr; | |
| 1354 } | |
| 1355 | |
| 1356 hr = update_key.SetValue(kRegValueUserId, user_id); | |
| 1357 if (FAILED(hr)) { | |
| 1358 return hr; | |
| 1359 } | |
| 1360 | |
| 1361 ++metric_opt_in_uid_generated; | |
| 1362 CORE_LOG(L3, (_T("[Create unique user ID: %s]"), user_id)); | |
| 1363 return S_OK; | |
| 1364 } | |
| 1365 | |
| 1366 void DeleteUserId(bool is_machine) { | |
| 1367 RegKey::DeleteValue(ConfigManager::Instance()->registry_update(is_machine), | |
| 1368 kRegValueUserId); | |
| 1369 } | |
| 1370 | |
| 1371 CString GetUserIdLazyInit(bool is_machine) { | |
| 1372 const ConfigManager& config_manager = *ConfigManager::Instance(); | |
| 1373 if (oem_install_utils::IsOemInstalling(is_machine) || | |
| 1374 !config_manager.CanCollectStats(is_machine)) { | |
| 1375 DeleteUserId(is_machine); | |
| 1376 return CString(); | |
| 1377 } | |
| 1378 | |
| 1379 if (!RegKey::HasValue(config_manager.registry_update(is_machine), | |
| 1380 kRegValueUserId)) { | |
| 1381 VERIFY1(SUCCEEDED(CreateUserId(is_machine))); | |
| 1382 } | |
| 1383 | |
| 1384 CString user_id; | |
| 1385 RegKey::GetValue(config_manager.registry_update(is_machine), | |
| 1386 kRegValueUserId, | |
| 1387 &user_id); | |
| 1388 return user_id; | |
| 1389 } | |
| 1390 | |
| 1391 } // namespace goopdate_utils | |
| 1392 | |
| 1393 } // namespace omaha | |
| OLD | NEW |