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 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") != 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 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 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) 1037