OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // chrome_tab.cc : Implementation of DLL Exports. | |
6 | |
7 // Need to include this before the ATL headers below. | |
8 #include "chrome_frame/chrome_tab.h" | |
9 | |
10 #include <atlsecurity.h> | |
11 #include <objbase.h> | |
12 | |
13 #include "base/at_exit.h" | |
14 #include "base/basictypes.h" | |
15 #include "base/command_line.h" | |
16 #include "base/file_util.h" | |
17 #include "base/file_version_info.h" | |
18 #include "base/i18n/icu_util.h" | |
19 #include "base/logging.h" | |
20 #include "base/logging_win.h" | |
21 #include "base/path_service.h" | |
22 #include "base/process/launch.h" | |
23 #include "base/strings/string16.h" | |
24 #include "base/strings/string_number_conversions.h" | |
25 #include "base/strings/string_piece.h" | |
26 #include "base/strings/string_util.h" | |
27 #include "base/strings/sys_string_conversions.h" | |
28 #include "base/strings/utf_string_conversions.h" | |
29 #include "base/win/registry.h" | |
30 #include "base/win/windows_version.h" | |
31 #include "chrome/common/chrome_constants.h" | |
32 #include "chrome/common/chrome_switches.h" | |
33 #include "chrome/installer/util/google_update_settings.h" | |
34 #include "chrome_frame/bho.h" | |
35 #include "chrome_frame/chrome_active_document.h" | |
36 #include "chrome_frame/chrome_frame_activex.h" | |
37 #include "chrome_frame/chrome_frame_automation.h" | |
38 #include "chrome_frame/chrome_frame_reporting.h" | |
39 #include "chrome_frame/chrome_launcher_utils.h" | |
40 #include "chrome_frame/chrome_protocol.h" | |
41 #include "chrome_frame/dll_redirector.h" | |
42 #include "chrome_frame/exception_barrier.h" | |
43 #include "chrome_frame/pin_module.h" | |
44 #include "chrome_frame/resource.h" | |
45 #include "chrome_frame/utils.h" | |
46 #include "components/variations/entropy_provider.h" | |
47 #include "grit/chrome_frame_resources.h" | |
48 #include "url/url_util.h" | |
49 | |
50 #if _ATL_VER >= 0x0C00 | |
51 // This was removed between the VS2010 version and the VS2013 version, and | |
52 // the unsuffixed version was repurposed to mean 'S'. | |
53 #define UpdateRegistryFromResourceS UpdateRegistryFromResource | |
54 #endif | |
55 | |
56 using base::win::RegKey; | |
57 | |
58 namespace { | |
59 | |
60 const wchar_t kInternetSettings[] = | |
61 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; | |
62 | |
63 const wchar_t kProtocolHandlers[] = | |
64 L"Software\\Classes\\Protocols\\Handler"; | |
65 | |
66 const wchar_t kRunOnce[] = | |
67 L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; | |
68 | |
69 const wchar_t kRunKeyName[] = L"ChromeFrameHelper"; | |
70 | |
71 const wchar_t kChromeFrameHelperExe[] = L"chrome_frame_helper.exe"; | |
72 const wchar_t kChromeFrameHelperStartupArg[] = L"--startup"; | |
73 | |
74 // Window class and window names. | |
75 // TODO(robertshield): These and other constants need to be refactored into | |
76 // a common chrome_frame_constants.h|cc and built into a separate lib | |
77 // (either chrome_frame_utils or make another one). | |
78 const wchar_t kChromeFrameHelperWindowClassName[] = | |
79 L"ChromeFrameHelperWindowClass"; | |
80 const wchar_t kChromeFrameHelperWindowName[] = | |
81 L"ChromeFrameHelperWindowName"; | |
82 | |
83 // {0562BFC3-2550-45b4-BD8E-A310583D3A6F} | |
84 const GUID kChromeFrameProvider = | |
85 { 0x562bfc3, 0x2550, 0x45b4, | |
86 { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } }; | |
87 | |
88 const wchar_t kPostPlatformUAKey[] = | |
89 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\" | |
90 L"User Agent\\Post Platform"; | |
91 const wchar_t kChromeFramePrefix[] = L"chromeframe/"; | |
92 | |
93 // See comments in DllGetClassObject. | |
94 LPFNGETCLASSOBJECT g_dll_get_class_object_redir_ptr = NULL; | |
95 | |
96 // This function has the side effect of initializing an unprotected | |
97 // vector pointer inside GoogleUrl. If this is called during DLL loading, | |
98 // it has the effect of avoiding an initialization race on that pointer. | |
99 // TODO(siggi): fix GoogleUrl. | |
100 void InitGoogleUrl() { | |
101 static const char kDummyUrl[] = "http://www.google.com"; | |
102 | |
103 url_util::IsStandard(kDummyUrl, | |
104 url_parse::MakeRange(0, arraysize(kDummyUrl))); | |
105 } | |
106 | |
107 class ChromeTabModule : public CAtlDllModuleT<ChromeTabModule> { | |
108 public: | |
109 typedef CAtlDllModuleT<ChromeTabModule> ParentClass; | |
110 | |
111 ChromeTabModule() : do_system_registration_(true), | |
112 crash_reporting_(NULL), | |
113 icu_initialized_(false) {} | |
114 | |
115 DECLARE_LIBID(LIBID_ChromeTabLib) | |
116 DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CHROMETAB, | |
117 "{FD9B1B31-F4D8-436A-8F4F-D3C2E36733D3}") | |
118 | |
119 // Override to add our SYSTIME binary value to registry scripts. | |
120 // See chrome_frame_activex.rgs for usage. | |
121 virtual HRESULT AddCommonRGSReplacements(IRegistrarBase* registrar) throw() { | |
122 HRESULT hr = ParentClass::AddCommonRGSReplacements(registrar); | |
123 | |
124 if (SUCCEEDED(hr)) { | |
125 SYSTEMTIME local_time; | |
126 ::GetSystemTime(&local_time); | |
127 std::string hex(base::HexEncode(&local_time, sizeof(local_time))); | |
128 base::StringPiece sp_hex(hex); | |
129 hr = registrar->AddReplacement(L"SYSTIME", | |
130 base::SysNativeMBToWide(sp_hex).c_str()); | |
131 DCHECK(SUCCEEDED(hr)); | |
132 } | |
133 | |
134 if (SUCCEEDED(hr)) { | |
135 base::FilePath app_path = | |
136 chrome_launcher::GetChromeExecutablePath().DirName(); | |
137 hr = registrar->AddReplacement(L"CHROME_APPPATH", | |
138 app_path.value().c_str()); | |
139 DCHECK(SUCCEEDED(hr)); | |
140 } | |
141 | |
142 if (SUCCEEDED(hr)) { | |
143 hr = registrar->AddReplacement(L"CHROME_APPNAME", | |
144 chrome::kBrowserProcessExecutableName); | |
145 DCHECK(SUCCEEDED(hr)); | |
146 | |
147 // Fill in VERSION from the VERSIONINFO stored in the DLL's resources. | |
148 scoped_ptr<FileVersionInfo> module_version_info( | |
149 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); | |
150 DCHECK(module_version_info != NULL); | |
151 std::wstring file_version(module_version_info->file_version()); | |
152 hr = registrar->AddReplacement(L"VERSION", file_version.c_str()); | |
153 DCHECK(SUCCEEDED(hr)); | |
154 } | |
155 | |
156 if (SUCCEEDED(hr)) { | |
157 // Add the directory of chrome_launcher.exe. This will be the same | |
158 // as the directory for the current DLL. | |
159 std::wstring module_dir; | |
160 base::FilePath module_path; | |
161 if (PathService::Get(base::FILE_MODULE, &module_path)) { | |
162 module_dir = module_path.DirName().value(); | |
163 } else { | |
164 NOTREACHED(); | |
165 } | |
166 hr = registrar->AddReplacement(L"CHROME_LAUNCHER_APPPATH", | |
167 module_dir.c_str()); | |
168 DCHECK(SUCCEEDED(hr)); | |
169 } | |
170 | |
171 if (SUCCEEDED(hr)) { | |
172 // Add the filename of chrome_launcher.exe | |
173 hr = registrar->AddReplacement(L"CHROME_LAUNCHER_APPNAME", | |
174 chrome_launcher::kLauncherExeBaseName); | |
175 DCHECK(SUCCEEDED(hr)); | |
176 } | |
177 | |
178 if (SUCCEEDED(hr)) { | |
179 // Add the registry hive to use. | |
180 // Note: This is ugly as hell. I'd rather use the pMapEntries parameter | |
181 // to CAtlModule::UpdateRegistryFromResource, unfortunately we have a | |
182 // few components that are registered by calling their | |
183 // static T::UpdateRegistry() methods directly, which doesn't allow | |
184 // pMapEntries to be passed through :-( | |
185 if (do_system_registration_) { | |
186 hr = registrar->AddReplacement(L"HIVE", L"HKLM"); | |
187 } else { | |
188 hr = registrar->AddReplacement(L"HIVE", L"HKCU"); | |
189 } | |
190 DCHECK(SUCCEEDED(hr)); | |
191 } | |
192 | |
193 if (SUCCEEDED(hr)) { | |
194 // Add the Chrome Frame CLSID. | |
195 wchar_t cf_clsid[64]; | |
196 StringFromGUID2(CLSID_ChromeFrame, &cf_clsid[0], arraysize(cf_clsid)); | |
197 hr = registrar->AddReplacement(L"CHROME_FRAME_CLSID", &cf_clsid[0]); | |
198 } | |
199 | |
200 return hr; | |
201 } | |
202 | |
203 // The module is "locked" when an object takes a reference on it. The first | |
204 // time it is locked, take a reference on crash reporting to bind its lifetime | |
205 // to the module and initialize ICU. | |
206 virtual LONG Lock() throw() { | |
207 LONG result = ParentClass::Lock(); | |
208 if (result == 1) { | |
209 DCHECK_EQ(crash_reporting_, | |
210 static_cast<chrome_frame::ScopedCrashReporting*>(NULL)); | |
211 crash_reporting_ = new chrome_frame::ScopedCrashReporting(); | |
212 | |
213 // Initialize ICU if this is the first time the module has been locked. | |
214 if (!icu_initialized_) { | |
215 icu_initialized_ = true; | |
216 // Best-effort since something is better than nothing here. | |
217 ignore_result(base::i18n::InitializeICU()); | |
218 } | |
219 } | |
220 return result; | |
221 } | |
222 | |
223 // The module is "unlocked" when an object that had a reference on it is | |
224 // destroyed. The last time it is unlocked, release the reference on crash | |
225 // reporting. | |
226 virtual LONG Unlock() throw() { | |
227 LONG result = ParentClass::Unlock(); | |
228 if (!result) { | |
229 DCHECK_NE(crash_reporting_, | |
230 static_cast<chrome_frame::ScopedCrashReporting*>(NULL)); | |
231 delete crash_reporting_; | |
232 crash_reporting_ = NULL; | |
233 } | |
234 return result; | |
235 } | |
236 | |
237 // See comments in AddCommonRGSReplacements | |
238 bool do_system_registration_; | |
239 | |
240 private: | |
241 // A scoper created when the module is initially locked and destroyed when it | |
242 // is finally unlocked. This is not a scoped_ptr since that could cause | |
243 // reporting to shut down at exit, which would lead to problems with the | |
244 // loader lock. | |
245 chrome_frame::ScopedCrashReporting* crash_reporting_; | |
246 | |
247 // Initially false, this is flipped to true to indicate that ICU has been | |
248 // initialized for the module. | |
249 bool icu_initialized_; | |
250 }; | |
251 | |
252 ChromeTabModule _AtlModule; | |
253 | |
254 base::AtExitManager* g_exit_manager = NULL; | |
255 | |
256 HRESULT RefreshElevationPolicy() { | |
257 const wchar_t kIEFrameDll[] = L"ieframe.dll"; | |
258 const char kIERefreshPolicy[] = "IERefreshElevationPolicy"; | |
259 HRESULT hr = E_NOTIMPL; | |
260 | |
261 // Stick an SEH in the chain to prevent the VEH from picking up on first | |
262 // chance exceptions caused by loading ieframe.dll. Use the vanilla | |
263 // ExceptionBarrier to report any exceptions that do make their way to us | |
264 // though. | |
265 ExceptionBarrier barrier; | |
266 | |
267 HMODULE ieframe_module = LoadLibrary(kIEFrameDll); | |
268 if (ieframe_module) { | |
269 typedef HRESULT (__stdcall *IERefreshPolicy)(); | |
270 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>( | |
271 GetProcAddress(ieframe_module, kIERefreshPolicy)); | |
272 | |
273 if (ie_refresh_policy) { | |
274 hr = ie_refresh_policy(); | |
275 } else { | |
276 hr = HRESULT_FROM_WIN32(GetLastError()); | |
277 } | |
278 | |
279 FreeLibrary(ieframe_module); | |
280 } else { | |
281 hr = HRESULT_FROM_WIN32(GetLastError()); | |
282 } | |
283 | |
284 return hr; | |
285 } | |
286 | |
287 // Experimental boot prefetch optimization for Chrome Frame | |
288 // | |
289 // If chrome is warmed up during a single reboot, it gets paged | |
290 // in for subsequent reboots and the cold startup times essentially | |
291 // look like warm times thereafter! The 'warm up' is done by | |
292 // setting up a 'RunOnce' key during DLLRegisterServer of | |
293 // npchrome_frame.dll. | |
294 // | |
295 // This works because chrome prefetch becomes part of boot | |
296 // prefetch file ntosboot-b00dfaad.pf and paged in on subsequent | |
297 // reboots. As long as the sytem does not undergo significant | |
298 // memory pressure those pages remain in memory and we get pretty | |
299 // amazing startup times, down to about 300 ms from 1200 ms | |
300 // | |
301 // The downside is: | |
302 // - Whether chrome frame is used or not, there's a read penalty | |
303 // (1200-300 =) 900 ms for every boot. | |
304 // - Heavy system memory usage after reboot will nullify the benefits | |
305 // but the user will still pay the cost. | |
306 // - Overall the time saved will always be less than total time spent | |
307 // paging in chrome | |
308 // - We are not sure when the chrome 'warm up' will age out from the | |
309 // boot prefetch file. | |
310 // | |
311 // The idea here is to try this out on chrome frame dev channel | |
312 // and see if it produces a significant drift in startup numbers. | |
313 HRESULT SetupRunOnce() { | |
314 HRESULT result = E_FAIL; | |
315 | |
316 base::string16 channel_name; | |
317 if (base::win::GetVersion() < base::win::VERSION_VISTA && | |
318 GoogleUpdateSettings::GetChromeChannelAndModifiers(true, &channel_name)) { | |
319 std::transform(channel_name.begin(), channel_name.end(), | |
320 channel_name.begin(), tolower); | |
321 // Use this only for the dev channel. | |
322 if (channel_name.find(L"dev") != base::string16::npos) { | |
323 HKEY hive = HKEY_CURRENT_USER; | |
324 if (IsSystemProcess()) { | |
325 // For system installs, our updates will be running as SYSTEM which | |
326 // makes writing to a RunOnce key under HKCU not so terribly useful. | |
327 hive = HKEY_LOCAL_MACHINE; | |
328 } | |
329 | |
330 RegKey run_once; | |
331 LONG ret = run_once.Create(hive, kRunOnce, KEY_READ | KEY_WRITE); | |
332 if (ret == ERROR_SUCCESS) { | |
333 CommandLine run_once_cmd(chrome_launcher::GetChromeExecutablePath()); | |
334 run_once_cmd.AppendSwitchASCII(switches::kAutomationClientChannelID, | |
335 "0"); | |
336 run_once_cmd.AppendSwitch(switches::kChromeFrame); | |
337 ret = run_once.WriteValue(L"A", | |
338 run_once_cmd.GetCommandLineString().c_str()); | |
339 } | |
340 result = HRESULT_FROM_WIN32(ret); | |
341 } else { | |
342 result = S_FALSE; | |
343 } | |
344 } else { | |
345 // We're on a non-XP version of Windows or on a stable channel. Nothing | |
346 // needs doing. | |
347 result = S_FALSE; | |
348 } | |
349 | |
350 return result; | |
351 } | |
352 | |
353 // Helper method called for user-level installs where we don't have admin | |
354 // permissions. Starts up the long running process and registers it to get it | |
355 // started at next boot. | |
356 HRESULT SetupUserLevelHelper() { | |
357 HRESULT hr = S_OK; | |
358 | |
359 // Remove existing run-at-startup entry. | |
360 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, kRunKeyName); | |
361 | |
362 // Build the chrome_frame_helper command line. | |
363 base::FilePath module_path; | |
364 base::FilePath helper_path; | |
365 if (PathService::Get(base::FILE_MODULE, &module_path)) { | |
366 module_path = module_path.DirName(); | |
367 helper_path = module_path.Append(kChromeFrameHelperExe); | |
368 if (!base::PathExists(helper_path)) { | |
369 // If we can't find the helper in the current directory, try looking | |
370 // one up (this is the layout in the build output folder). | |
371 module_path = module_path.DirName(); | |
372 helper_path = module_path.Append(kChromeFrameHelperExe); | |
373 DCHECK(base::PathExists(helper_path)) << | |
374 "Could not find chrome_frame_helper.exe."; | |
375 } | |
376 | |
377 // Find window handle of existing instance. | |
378 HWND old_window = FindWindow(kChromeFrameHelperWindowClassName, | |
379 kChromeFrameHelperWindowName); | |
380 | |
381 if (base::PathExists(helper_path)) { | |
382 std::wstring helper_path_cmd(L"\""); | |
383 helper_path_cmd += helper_path.value(); | |
384 helper_path_cmd += L"\" "; | |
385 helper_path_cmd += kChromeFrameHelperStartupArg; | |
386 | |
387 // Add new run-at-startup entry. | |
388 if (!base::win::AddCommandToAutoRun(HKEY_CURRENT_USER, kRunKeyName, | |
389 helper_path_cmd)) { | |
390 hr = E_FAIL; | |
391 LOG(ERROR) << "Could not add helper process to auto run key."; | |
392 } | |
393 | |
394 // Start new instance. | |
395 base::LaunchOptions options; | |
396 options.start_hidden = true; | |
397 bool launched = base::LaunchProcess(helper_path.value(), options, NULL); | |
398 if (!launched) { | |
399 hr = E_FAIL; | |
400 PLOG(DFATAL) << "Could not launch helper process."; | |
401 } | |
402 | |
403 // Kill old instance using window handle. | |
404 if (IsWindow(old_window)) { | |
405 BOOL result = PostMessage(old_window, WM_CLOSE, 0, 0); | |
406 if (!result) { | |
407 PLOG(ERROR) << "Failed to post close message to old helper process: "; | |
408 } | |
409 } | |
410 } else { | |
411 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | |
412 } | |
413 } else { | |
414 hr = E_UNEXPECTED; | |
415 NOTREACHED(); | |
416 } | |
417 | |
418 return hr; | |
419 } | |
420 | |
421 // To delete the user agent, set value to NULL. | |
422 // The is_system parameter indicates whether this is a per machine or a per | |
423 // user installation. | |
424 HRESULT SetChromeFrameUA(bool is_system, const wchar_t* value) { | |
425 HRESULT hr = E_FAIL; | |
426 HKEY parent_hive = is_system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | |
427 | |
428 RegKey ua_key; | |
429 LONG reg_result = ua_key.Create(parent_hive, kPostPlatformUAKey, | |
430 KEY_READ | KEY_WRITE); | |
431 if (reg_result == ERROR_SUCCESS) { | |
432 // Make sure that we unregister ChromeFrame UA strings registered previously | |
433 wchar_t value_name[MAX_PATH + 1] = {}; | |
434 wchar_t value_data[MAX_PATH + 1] = {}; | |
435 | |
436 DWORD value_index = 0; | |
437 while (value_index < ua_key.GetValueCount()) { | |
438 DWORD name_size = arraysize(value_name); | |
439 DWORD value_size = arraysize(value_data); | |
440 DWORD type = 0; | |
441 LRESULT ret = ::RegEnumValue(ua_key.Handle(), value_index, value_name, | |
442 &name_size, NULL, &type, | |
443 reinterpret_cast<BYTE*>(value_data), | |
444 &value_size); | |
445 if (ret == ERROR_SUCCESS) { | |
446 if (StartsWith(value_name, kChromeFramePrefix, false)) { | |
447 ua_key.DeleteValue(value_name); | |
448 } else { | |
449 ++value_index; | |
450 } | |
451 } else { | |
452 break; | |
453 } | |
454 } | |
455 | |
456 std::wstring chrome_frame_ua_value_name = kChromeFramePrefix; | |
457 chrome_frame_ua_value_name += GetCurrentModuleVersion(); | |
458 if (value) { | |
459 ua_key.WriteValue(chrome_frame_ua_value_name.c_str(), value); | |
460 } | |
461 hr = S_OK; | |
462 } else { | |
463 DLOG(ERROR) << __FUNCTION__ << ": " << kPostPlatformUAKey | |
464 << ", error code = " << reg_result; | |
465 hr = HRESULT_FROM_WIN32(reg_result); | |
466 } | |
467 return hr; | |
468 } | |
469 | |
470 class SecurityDescBackup { | |
471 public: | |
472 explicit SecurityDescBackup(const std::wstring& backup_key) | |
473 : backup_key_name_(backup_key) {} | |
474 ~SecurityDescBackup() {} | |
475 | |
476 // Save given security descriptor to the backup key. | |
477 bool SaveSecurity(const CSecurityDesc& sd) { | |
478 CString str; | |
479 if (!sd.ToString(&str)) | |
480 return false; | |
481 | |
482 RegKey backup_key(HKEY_LOCAL_MACHINE, backup_key_name_.c_str(), | |
483 KEY_READ | KEY_WRITE); | |
484 if (backup_key.Valid()) { | |
485 return backup_key.WriteValue(NULL, str.GetString()) == ERROR_SUCCESS; | |
486 } | |
487 | |
488 return false; | |
489 } | |
490 | |
491 // Restore security descriptor from backup key to given key name. | |
492 bool RestoreSecurity(const wchar_t* key_name) { | |
493 std::wstring sddl; | |
494 if (!ReadBackupKey(&sddl)) | |
495 return false; | |
496 | |
497 // Create security descriptor from string. | |
498 CSecurityDesc sd; | |
499 if (!sd.FromString(sddl.c_str())) | |
500 return false; | |
501 | |
502 bool result = true; | |
503 // Restore DACL and Owner of the key from saved security descriptor. | |
504 CDacl dacl; | |
505 CSid owner; | |
506 sd.GetDacl(&dacl); | |
507 sd.GetOwner(&owner); | |
508 | |
509 DWORD error = ::SetNamedSecurityInfo(const_cast<wchar_t*>(key_name), | |
510 SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, | |
511 const_cast<SID*>(owner.GetPSID()), NULL, | |
512 const_cast<ACL*>(dacl.GetPACL()), NULL); | |
513 | |
514 DeleteBackupKey(); | |
515 return (error == ERROR_SUCCESS); | |
516 } | |
517 | |
518 private: | |
519 // Read SDDL string from backup key | |
520 bool ReadBackupKey(std::wstring* sddl) { | |
521 RegKey backup_key(HKEY_LOCAL_MACHINE, backup_key_name_.c_str(), KEY_READ); | |
522 if (!backup_key.Valid()) | |
523 return false; | |
524 | |
525 DWORD len = 0; | |
526 DWORD reg_type = REG_NONE; | |
527 if (backup_key.ReadValue(NULL, NULL, &len, ®_type) != ERROR_SUCCESS) | |
528 return false; | |
529 DCHECK_EQ(0u, len % sizeof(wchar_t)); | |
530 | |
531 if ((len == 0) || (reg_type != REG_SZ)) | |
532 return false; | |
533 | |
534 size_t wchar_count = 1 + len / sizeof(wchar_t); | |
535 if (backup_key.ReadValue(NULL, WriteInto(sddl, wchar_count), &len, | |
536 ®_type) != ERROR_SUCCESS) { | |
537 return false; | |
538 } | |
539 | |
540 return true; | |
541 } | |
542 | |
543 void DeleteBackupKey() { | |
544 ::RegDeleteKey(HKEY_LOCAL_MACHINE, backup_key_name_.c_str()); | |
545 } | |
546 | |
547 std::wstring backup_key_name_; | |
548 }; | |
549 | |
550 struct TokenWithPrivileges { | |
551 TokenWithPrivileges() { | |
552 token_.GetEffectiveToken(TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY); | |
553 token_.GetUser(&user_); | |
554 } | |
555 | |
556 ~TokenWithPrivileges() { | |
557 token_.EnableDisablePrivileges(take_ownership_); | |
558 token_.EnableDisablePrivileges(restore_); | |
559 } | |
560 | |
561 bool EnablePrivileges() { | |
562 if (take_ownership_.GetCount() == 0) | |
563 if (!token_.EnablePrivilege(L"SeTakeOwnershipPrivilege", | |
564 &take_ownership_)) | |
565 return false; | |
566 | |
567 if (restore_.GetCount() == 0) | |
568 if (!token_.EnablePrivilege(L"SeRestorePrivilege", &restore_)) | |
569 return false; | |
570 | |
571 return true; | |
572 } | |
573 | |
574 const CSid& GetUser() const { | |
575 return user_; | |
576 } | |
577 | |
578 private: | |
579 CAccessToken token_; | |
580 CTokenPrivileges take_ownership_; | |
581 CTokenPrivileges restore_; | |
582 CSid user_; | |
583 }; | |
584 | |
585 const wchar_t* const kMimeHandlerKeyValues[] = { | |
586 L"ChromeTab.ChromeActiveDocument", | |
587 L"ChromeTab.ChromeActiveDocument.1", | |
588 }; | |
589 | |
590 // Returns true if the values are present or absent in |root_key|'s Secure Mime | |
591 // Handlers key based on |for_installed|. Returns false if the values are not as | |
592 // expected or if an error occurred. | |
593 bool MimeHandlerKeyIsConfigured(bool for_install, HKEY root_key) { | |
594 base::string16 key_name(kInternetSettings); | |
595 key_name.append(L"\\Secure Mime Handlers"); | |
596 RegKey key(root_key, key_name.c_str(), KEY_QUERY_VALUE); | |
597 if (!key.Valid()) | |
598 return false; | |
599 | |
600 for (size_t i = 0; i < arraysize(kMimeHandlerKeyValues); ++i) { | |
601 DWORD value = 0; | |
602 LONG result = key.ReadValueDW(kMimeHandlerKeyValues[i], &value); | |
603 if (for_install) { | |
604 if (result != ERROR_SUCCESS || value != 1) | |
605 return false; | |
606 } else { | |
607 if (result != ERROR_FILE_NOT_FOUND) | |
608 return false; | |
609 } | |
610 } | |
611 return true; | |
612 } | |
613 | |
614 HRESULT SetOrDeleteMimeHandlerKey(bool set, HKEY root_key) { | |
615 base::string16 key_name(kInternetSettings); | |
616 key_name.append(L"\\Secure Mime Handlers"); | |
617 RegKey key(root_key, key_name.c_str(), KEY_SET_VALUE); | |
618 if (!key.Valid()) | |
619 return false; | |
620 | |
621 HRESULT result = S_OK; | |
622 for (size_t i = 0; i < arraysize(kMimeHandlerKeyValues); ++i) { | |
623 LONG intermediate = set ? | |
624 key.WriteValue(kMimeHandlerKeyValues[i], 1) : | |
625 key.DeleteValue(kMimeHandlerKeyValues[i]); | |
626 if (intermediate != ERROR_SUCCESS && result == S_OK) | |
627 result = HRESULT_FROM_WIN32(intermediate); | |
628 } | |
629 return result; | |
630 } | |
631 | |
632 void OnPinModule() { | |
633 // Pin crash reporting by leaking a reference. | |
634 ignore_result(new chrome_frame::ScopedCrashReporting()); | |
635 } | |
636 | |
637 // Chrome Frame registration functions. | |
638 //----------------------------------------------------------------------------- | |
639 HRESULT RegisterSecuredMimeHandler(bool enable, bool is_system) { | |
640 if (MimeHandlerKeyIsConfigured(enable, HKEY_LOCAL_MACHINE)) | |
641 return S_OK; | |
642 if (!is_system) | |
643 return SetOrDeleteMimeHandlerKey(enable, HKEY_CURRENT_USER); | |
644 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
645 return SetOrDeleteMimeHandlerKey(enable, HKEY_LOCAL_MACHINE); | |
646 | |
647 std::wstring mime_key = kInternetSettings; | |
648 mime_key.append(L"\\Secure Mime Handlers"); | |
649 std::wstring backup_key = kInternetSettings; | |
650 backup_key.append(L"\\__backup_SMH__"); | |
651 std::wstring object_name = L"MACHINE\\"; | |
652 object_name.append(mime_key); | |
653 | |
654 TokenWithPrivileges token_; | |
655 if (!token_.EnablePrivileges()) | |
656 return E_ACCESSDENIED; | |
657 | |
658 // If there is a backup key - something bad happened; try to restore | |
659 // security on "Secure Mime Handlers" from the backup. | |
660 SecurityDescBackup backup(backup_key); | |
661 backup.RestoreSecurity(object_name.c_str()); | |
662 | |
663 // Read old security descriptor of the Mime key first. | |
664 CSecurityDesc sd; | |
665 if (!AtlGetSecurityDescriptor(object_name.c_str(), SE_REGISTRY_KEY, &sd)) { | |
666 return E_FAIL; | |
667 } | |
668 | |
669 backup.SaveSecurity(sd); | |
670 HRESULT hr = E_FAIL; | |
671 // set new owner | |
672 if (AtlSetOwnerSid(object_name.c_str(), SE_REGISTRY_KEY, token_.GetUser())) { | |
673 // set new dacl | |
674 CDacl new_dacl; | |
675 sd.GetDacl(&new_dacl); | |
676 new_dacl.AddAllowedAce(token_.GetUser(), GENERIC_WRITE | GENERIC_READ); | |
677 if (AtlSetDacl(object_name.c_str(), SE_REGISTRY_KEY, new_dacl)) { | |
678 hr = SetOrDeleteMimeHandlerKey(enable, HKEY_LOCAL_MACHINE); | |
679 } | |
680 } | |
681 | |
682 backup.RestoreSecurity(object_name.c_str()); | |
683 return hr; | |
684 } | |
685 | |
686 HRESULT RegisterActiveDoc(bool reg, bool is_system) { | |
687 // We have to call the static T::UpdateRegistry function instead of | |
688 // _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ACTIVEDOC, reg) | |
689 // because there is specific OLEMISC replacement. | |
690 return ChromeActiveDocument::UpdateRegistry(reg); | |
691 } | |
692 | |
693 HRESULT RegisterActiveX(bool reg, bool is_system) { | |
694 // We have to call the static T::UpdateRegistry function instead of | |
695 // _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ACTIVEX, reg) | |
696 // because there is specific OLEMISC replacement. | |
697 return ChromeFrameActivex::UpdateRegistry(reg); | |
698 } | |
699 | |
700 HRESULT RegisterElevationPolicy(bool reg, bool is_system) { | |
701 HRESULT hr = S_OK; | |
702 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
703 // Register the elevation policy. This must succeed for Chrome Frame to | |
704 // be able launch Chrome when running in low-integrity IE. | |
705 hr = _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ELEVATION, reg); | |
706 if (SUCCEEDED(hr)) { | |
707 // Ignore failures since old versions of IE 7 (e.g., 7.0.6000.16386, which | |
708 // shipped with Vista RTM) do not export IERefreshElevationPolicy. | |
709 RefreshElevationPolicy(); | |
710 } | |
711 } | |
712 return hr; | |
713 } | |
714 | |
715 HRESULT RegisterProtocol(bool reg, bool is_system) { | |
716 return _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEPROTOCOL, reg); | |
717 } | |
718 | |
719 HRESULT RegisterBhoClsid(bool reg, bool is_system) { | |
720 return Bho::UpdateRegistry(reg); | |
721 } | |
722 | |
723 HRESULT RegisterBhoIE(bool reg, bool is_system) { | |
724 if (is_system) { | |
725 return _AtlModule.UpdateRegistryFromResourceS(IDR_REGISTER_BHO, reg); | |
726 } else { | |
727 if (reg) { | |
728 // Setup the long running process: | |
729 return SetupUserLevelHelper(); | |
730 } else { | |
731 // Unschedule the user-level helper. Note that we don't kill it here | |
732 // so that during updates we don't have a time window with no running | |
733 // helper. Uninstalls and updates will explicitly kill the helper from | |
734 // within the installer. Unregister existing run-at-startup entry. | |
735 return base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, | |
736 kRunKeyName) ? S_OK : E_FAIL; | |
737 } | |
738 } | |
739 } | |
740 | |
741 HRESULT RegisterTypeLib(bool reg, bool is_system) { | |
742 if (reg && !is_system) { | |
743 // Enables the RegisterTypeLib Function function to override default | |
744 // registry mappings under Windows Vista Service Pack 1 (SP1), | |
745 // Windows Server 2008, and later operating system versions | |
746 typedef void (WINAPI* OaEnablePerUserTypeLibReg)(void); | |
747 OaEnablePerUserTypeLibReg per_user_typelib_func = | |
748 reinterpret_cast<OaEnablePerUserTypeLibReg>( | |
749 GetProcAddress(GetModuleHandle(L"oleaut32.dll"), | |
750 "OaEnablePerUserTLibRegistration")); | |
751 if (per_user_typelib_func) { | |
752 (*per_user_typelib_func)(); | |
753 } | |
754 } | |
755 return reg ? | |
756 UtilRegisterTypeLib(_AtlComModule.m_hInstTypeLib, | |
757 NULL, !is_system) : | |
758 UtilUnRegisterTypeLib(_AtlComModule.m_hInstTypeLib, | |
759 NULL, !is_system); | |
760 } | |
761 | |
762 HRESULT RegisterLegacyNPAPICleanup(bool reg, bool is_system) { | |
763 if (!reg) { | |
764 _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_NPAPI, reg); | |
765 UtilRemovePersistentNPAPIMarker(); | |
766 } | |
767 // Ignore failures. | |
768 return S_OK; | |
769 } | |
770 | |
771 HRESULT RegisterAppId(bool reg, bool is_system) { | |
772 return _AtlModule.UpdateRegistryAppId(reg); | |
773 } | |
774 | |
775 HRESULT RegisterUserAgent(bool reg, bool is_system) { | |
776 if (reg) { | |
777 return SetChromeFrameUA(is_system, L"1"); | |
778 } else { | |
779 return SetChromeFrameUA(is_system, NULL); | |
780 } | |
781 } | |
782 | |
783 enum RegistrationStepId { | |
784 kStepSecuredMimeHandler = 0, | |
785 kStepActiveDoc = 1, | |
786 kStepActiveX = 2, | |
787 kStepElevationPolicy = 3, | |
788 kStepProtocol = 4, | |
789 kStepBhoClsid = 5, | |
790 kStepBhoRegistration = 6, | |
791 kStepRegisterTypeLib = 7, | |
792 kStepNpapiCleanup = 8, | |
793 kStepAppId = 9, | |
794 kStepUserAgent = 10, | |
795 kStepEnd = 11 | |
796 }; | |
797 | |
798 enum RegistrationFlags { | |
799 ACTIVEX = 0x0001, | |
800 ACTIVEDOC = 0x0002, | |
801 GCF_PROTOCOL = 0x0004, | |
802 BHO_CLSID = 0x0008, | |
803 BHO_REGISTRATION = 0x0010, | |
804 TYPELIB = 0x0020, | |
805 | |
806 ALL = 0xFFFF | |
807 }; | |
808 | |
809 // Mux the failure step into the hresult. We take only the first four bits | |
810 // and stick those into the top four bits of the facility code. We also set the | |
811 // Customer bit to be polite. Graphically, we write our error code to the | |
812 // bits marked with ^: | |
813 // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | |
814 // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 | |
815 // +---+-+-+-----------------------+-------------------------------+ | |
816 // |Sev|C|R| Facility | Code | | |
817 // +---+-+-+-----------------------+-------------------------------+ | |
818 // ^ ^ ^ ^ ^ | |
819 // See http://msdn.microsoft.com/en-us/library/cc231198(PROT.10).aspx for | |
820 // more details on HRESULTS. | |
821 // | |
822 // The resulting error can be extracted by: | |
823 // error_code = (fiddled_hr & 0x07800000) >> 23 | |
824 HRESULT MuxErrorIntoHRESULT(HRESULT hr, int error_code) { | |
825 DCHECK_GE(error_code, 0); | |
826 DCHECK_LT(error_code, kStepEnd); | |
827 COMPILE_ASSERT(kStepEnd <= 0xF, update_error_muxing_too_many_steps); | |
828 | |
829 // Check that our four desired bits are clear. | |
830 // 0xF87FFFFF == 11111000011111111111111111111111 | |
831 DCHECK_EQ(static_cast<HRESULT>(hr & 0xF87FFFFF), hr); | |
832 | |
833 HRESULT fiddled_hr = ((error_code & 0xF) << 23) | hr; | |
834 fiddled_hr |= 1 << 29; // Set the customer bit. | |
835 | |
836 return fiddled_hr; | |
837 } | |
838 | |
839 HRESULT CustomRegistration(uint16 reg_flags, bool reg, bool is_system) { | |
840 if (reg && (reg_flags & (ACTIVEDOC | ACTIVEX))) | |
841 reg_flags |= (TYPELIB | GCF_PROTOCOL); | |
842 | |
843 // Set the flag that gets checked in AddCommonRGSReplacements before doing | |
844 // registration work. | |
845 _AtlModule.do_system_registration_ = is_system; | |
846 | |
847 typedef HRESULT (*RegistrationFn)(bool reg, bool is_system); | |
848 struct RegistrationStep { | |
849 RegistrationStepId id; | |
850 uint16 condition; | |
851 RegistrationFn func; | |
852 }; | |
853 static const RegistrationStep registration_steps[] = { | |
854 { kStepSecuredMimeHandler, ACTIVEDOC, &RegisterSecuredMimeHandler }, | |
855 { kStepActiveDoc, ACTIVEDOC, &RegisterActiveDoc }, | |
856 { kStepActiveX, ACTIVEX, &RegisterActiveX }, | |
857 { kStepElevationPolicy, (ACTIVEDOC | ACTIVEX), &RegisterElevationPolicy }, | |
858 { kStepProtocol, GCF_PROTOCOL, &RegisterProtocol }, | |
859 { kStepBhoClsid, BHO_CLSID, &RegisterBhoClsid }, | |
860 { kStepBhoRegistration, BHO_REGISTRATION, &RegisterBhoIE }, | |
861 { kStepRegisterTypeLib, TYPELIB, &RegisterTypeLib }, | |
862 { kStepNpapiCleanup, ALL, &RegisterLegacyNPAPICleanup }, | |
863 { kStepAppId, ALL, &RegisterAppId }, | |
864 { kStepUserAgent, ALL, &RegisterUserAgent } | |
865 }; | |
866 | |
867 HRESULT hr = S_OK; | |
868 | |
869 bool rollback = false; | |
870 int failure_step = 0; | |
871 HRESULT step_hr = S_OK; | |
872 for (int step = 0; step < arraysize(registration_steps); ++step) { | |
873 if ((reg_flags & registration_steps[step].condition) != 0) { | |
874 step_hr = registration_steps[step].func(reg, is_system); | |
875 if (FAILED(step_hr)) { | |
876 // Store only the first failing HRESULT with the step value muxed in. | |
877 if (hr == S_OK) { | |
878 hr = MuxErrorIntoHRESULT(step_hr, step); | |
879 } | |
880 | |
881 // On registration if a step fails, we abort and rollback. | |
882 if (reg) { | |
883 rollback = true; | |
884 failure_step = step; | |
885 break; | |
886 } | |
887 } | |
888 } | |
889 } | |
890 | |
891 if (rollback) { | |
892 DCHECK(reg); | |
893 // Rollback the failing action and all preceding ones. | |
894 for (int step = failure_step; step >= 0; --step) { | |
895 registration_steps[step].func(!reg, is_system); | |
896 } | |
897 } | |
898 | |
899 return hr; | |
900 } | |
901 | |
902 } // namespace | |
903 | |
904 // DLL Entry Point | |
905 extern "C" BOOL WINAPI DllMain(HINSTANCE instance, | |
906 DWORD reason, | |
907 LPVOID reserved) { | |
908 UNREFERENCED_PARAMETER(instance); | |
909 if (reason == DLL_PROCESS_ATTACH) { | |
910 #if _ATL_VER < 0x0C00 && !defined(NDEBUG) | |
911 // Silence traces from the ATL registrar to reduce the log noise. | |
912 ATL::CTrace::s_trace.ChangeCategory(atlTraceRegistrar, 0, | |
913 ATLTRACESTATUS_DISABLED); | |
914 #endif | |
915 InitGoogleUrl(); | |
916 | |
917 g_exit_manager = new base::AtExitManager(); | |
918 CommandLine::Init(0, NULL); | |
919 logging::LoggingSettings settings; | |
920 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; | |
921 logging::InitLogging(settings); | |
922 | |
923 // Log the same items as Chrome. | |
924 logging::SetLogItems(true, // enable_process_id | |
925 true, // enable_thread_id | |
926 false, // enable_timestamp | |
927 true); // enable_tickcount | |
928 | |
929 DllRedirector* dll_redirector = DllRedirector::GetInstance(); | |
930 DCHECK(dll_redirector); | |
931 | |
932 if (!dll_redirector->RegisterAsFirstCFModule()) { | |
933 // Someone else was here first, try and get a pointer to their | |
934 // DllGetClassObject export: | |
935 g_dll_get_class_object_redir_ptr = | |
936 dll_redirector->GetDllGetClassObjectPtr(); | |
937 DCHECK(g_dll_get_class_object_redir_ptr != NULL) | |
938 << "Found CF module with no DllGetClassObject export."; | |
939 } | |
940 | |
941 // Enable trace control and transport through event tracing for Windows. | |
942 logging::LogEventProvider::Initialize(kChromeFrameProvider); | |
943 | |
944 // Set a callback so that crash reporting can be pinned when the module is | |
945 // pinned. | |
946 chrome_frame::SetPinModuleCallback(&OnPinModule); | |
947 } else if (reason == DLL_PROCESS_DETACH) { | |
948 DllRedirector* dll_redirector = DllRedirector::GetInstance(); | |
949 DCHECK(dll_redirector); | |
950 dll_redirector->UnregisterAsFirstCFModule(); | |
951 | |
952 g_patch_helper.UnpatchIfNeeded(); | |
953 | |
954 delete g_exit_manager; | |
955 g_exit_manager = NULL; | |
956 } | |
957 return _AtlModule.DllMain(reason, reserved); | |
958 } | |
959 | |
960 // Used to determine whether the DLL can be unloaded by OLE | |
961 STDAPI DllCanUnloadNow() { | |
962 return _AtlModule.DllCanUnloadNow(); | |
963 } | |
964 | |
965 // Returns a class factory to create an object of the requested type | |
966 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { | |
967 chrome_frame::ScopedCrashReporting crash_reporting; | |
968 | |
969 // IE 11+ are unsupported. | |
970 if (GetIEVersion() > IE_10) { | |
971 return CLASS_E_CLASSNOTAVAILABLE; | |
972 } | |
973 | |
974 // If we found another module present when we were loaded, then delegate to | |
975 // that: | |
976 if (g_dll_get_class_object_redir_ptr) { | |
977 return g_dll_get_class_object_redir_ptr(rclsid, riid, ppv); | |
978 } | |
979 | |
980 // Enable sniffing and switching only if asked for BHO | |
981 // (we use BHO to get loaded in IE). | |
982 if (rclsid == CLSID_ChromeFrameBHO) { | |
983 g_patch_helper.InitializeAndPatchProtocolsIfNeeded(); | |
984 } | |
985 | |
986 return _AtlModule.DllGetClassObject(rclsid, riid, ppv); | |
987 } | |
988 | |
989 // DllRegisterServer - Adds entries to the system registry | |
990 STDAPI DllRegisterServer() { | |
991 chrome_frame::ScopedCrashReporting crash_reporting; | |
992 uint16 flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL | | |
993 BHO_CLSID | BHO_REGISTRATION; | |
994 | |
995 HRESULT hr = CustomRegistration(flags, true, true); | |
996 if (SUCCEEDED(hr)) { | |
997 SetupRunOnce(); | |
998 } | |
999 | |
1000 return hr; | |
1001 } | |
1002 | |
1003 // DllUnregisterServer - Removes entries from the system registry | |
1004 STDAPI DllUnregisterServer() { | |
1005 chrome_frame::ScopedCrashReporting crash_reporting; | |
1006 HRESULT hr = CustomRegistration(ALL, false, true); | |
1007 return hr; | |
1008 } | |
1009 | |
1010 // DllRegisterUserServer - Adds entries to the HKCU hive in the registry. | |
1011 STDAPI DllRegisterUserServer() { | |
1012 chrome_frame::ScopedCrashReporting crash_reporting; | |
1013 UINT flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL | | |
1014 BHO_CLSID | BHO_REGISTRATION; | |
1015 | |
1016 HRESULT hr = CustomRegistration(flags, TRUE, false); | |
1017 if (SUCCEEDED(hr)) { | |
1018 SetupRunOnce(); | |
1019 } | |
1020 | |
1021 return hr; | |
1022 } | |
1023 | |
1024 // DllUnregisterUserServer - Removes entries from the HKCU hive in the registry. | |
1025 STDAPI DllUnregisterUserServer() { | |
1026 chrome_frame::ScopedCrashReporting crash_reporting; | |
1027 HRESULT hr = CustomRegistration(ALL, FALSE, false); | |
1028 return hr; | |
1029 } | |
1030 | |
1031 // Object entries go here instead of with each object, so that we can move | |
1032 // the objects to a lib. Also reduces magic. | |
1033 OBJECT_ENTRY_AUTO(CLSID_ChromeFrameBHO, Bho) | |
1034 OBJECT_ENTRY_AUTO(__uuidof(ChromeActiveDocument), ChromeActiveDocument) | |
1035 OBJECT_ENTRY_AUTO(__uuidof(ChromeFrame), ChromeFrameActivex) | |
1036 OBJECT_ENTRY_AUTO(__uuidof(ChromeProtocol), ChromeProtocol) | |
OLD | NEW |