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