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 |