| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/common/sandbox_win.h" | 5 #include "content/common/sandbox_win.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/base_switches.h" | 9 #include "base/base_switches.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 #include "content/public/common/content_client.h" | 24 #include "content/public/common/content_client.h" |
| 25 #include "content/public/common/content_switches.h" | 25 #include "content/public/common/content_switches.h" |
| 26 #include "content/public/common/sandbox_init.h" | 26 #include "content/public/common/sandbox_init.h" |
| 27 #include "content/public/common/sandboxed_process_launcher_delegate.h" | 27 #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| 28 #include "sandbox/win/src/process_mitigations.h" | 28 #include "sandbox/win/src/process_mitigations.h" |
| 29 #include "sandbox/win/src/sandbox.h" | 29 #include "sandbox/win/src/sandbox.h" |
| 30 #include "sandbox/win/src/sandbox_nt_util.h" | 30 #include "sandbox/win/src/sandbox_nt_util.h" |
| 31 #include "sandbox/win/src/win_utils.h" | 31 #include "sandbox/win/src/win_utils.h" |
| 32 #include "ui/gfx/win/dpi.h" | 32 #include "ui/gfx/win/dpi.h" |
| 33 | 33 |
| 34 static sandbox::BrokerServices* g_broker_services = NULL; | 34 static sandbox::BrokerServices* g_broker_services = nullptr; |
| 35 static sandbox::TargetServices* g_target_services = NULL; | 35 static sandbox::TargetServices* g_target_services = nullptr; |
| 36 | 36 |
| 37 namespace content { | 37 namespace content { |
| 38 namespace { | 38 namespace { |
| 39 | 39 |
| 40 // The DLLs listed here are known (or under strong suspicion) of causing crashes | 40 // The DLLs listed here are known (or under strong suspicion) of causing crashes |
| 41 // when they are loaded in the renderer. Note: at runtime we generate short | 41 // when they are loaded in the renderer. Note: at runtime we generate short |
| 42 // versions of the dll name only if the dll has an extension. | 42 // versions of the dll name only if the dll has an extension. |
| 43 // For more information about how this list is generated, and how to get off | 43 // For more information about how this list is generated, and how to get off |
| 44 // of it, see: | 44 // of it, see: |
| 45 // https://sites.google.com/a/chromium.org/dev/Home/third-party-developers | 45 // https://sites.google.com/a/chromium.org/dev/Home/third-party-developers |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 base::FilePath fname(path); | 176 base::FilePath fname(path); |
| 177 return (fname.BaseName().value() == module_name); | 177 return (fname.BaseName().value() == module_name); |
| 178 } | 178 } |
| 179 | 179 |
| 180 // Adds a single dll by |module_name| into the |policy| blacklist. | 180 // Adds a single dll by |module_name| into the |policy| blacklist. |
| 181 // If |check_in_browser| is true we only add an unload policy only if the dll | 181 // If |check_in_browser| is true we only add an unload policy only if the dll |
| 182 // is also loaded in this process. | 182 // is also loaded in this process. |
| 183 void BlacklistAddOneDll(const wchar_t* module_name, | 183 void BlacklistAddOneDll(const wchar_t* module_name, |
| 184 bool check_in_browser, | 184 bool check_in_browser, |
| 185 sandbox::TargetPolicy* policy) { | 185 sandbox::TargetPolicy* policy) { |
| 186 HMODULE module = check_in_browser ? ::GetModuleHandleW(module_name) : NULL; | 186 HMODULE module = check_in_browser ? ::GetModuleHandleW(module_name) : nullptr; |
| 187 if (!module) { | 187 if (!module) { |
| 188 // The module could have been loaded with a 8.3 short name. We check | 188 // The module could have been loaded with a 8.3 short name. We check |
| 189 // the three most common cases: 'thelongname.dll' becomes | 189 // the three most common cases: 'thelongname.dll' becomes |
| 190 // 'thelon~1.dll', 'thelon~2.dll' and 'thelon~3.dll'. | 190 // 'thelon~1.dll', 'thelon~2.dll' and 'thelon~3.dll'. |
| 191 std::wstring name(module_name); | 191 std::wstring name(module_name); |
| 192 size_t period = name.rfind(L'.'); | 192 size_t period = name.rfind(L'.'); |
| 193 DCHECK_NE(std::string::npos, period); | 193 DCHECK_NE(std::string::npos, period); |
| 194 DCHECK_LE(3U, (name.size() - period)); | 194 DCHECK_LE(3U, (name.size() - period)); |
| 195 if (period <= 8) | 195 if (period <= 8) |
| 196 return; | 196 return; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 249 if (!cmd_line.HasSwitch(switches::kAllowNoSandboxJob)) | 249 if (!cmd_line.HasSwitch(switches::kAllowNoSandboxJob)) |
| 250 return true; | 250 return true; |
| 251 | 251 |
| 252 // Windows 8 allows nested jobs so we don't need to check if we are in other | 252 // Windows 8 allows nested jobs so we don't need to check if we are in other |
| 253 // job. | 253 // job. |
| 254 if (base::win::GetVersion() >= base::win::VERSION_WIN8) | 254 if (base::win::GetVersion() >= base::win::VERSION_WIN8) |
| 255 return true; | 255 return true; |
| 256 | 256 |
| 257 BOOL in_job = true; | 257 BOOL in_job = true; |
| 258 // Either there is no job yet associated so we must add our job, | 258 // Either there is no job yet associated so we must add our job, |
| 259 if (!::IsProcessInJob(::GetCurrentProcess(), NULL, &in_job)) | 259 if (!::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job)) |
| 260 NOTREACHED() << "IsProcessInJob failed. " << GetLastError(); | 260 NOTREACHED() << "IsProcessInJob failed. " << GetLastError(); |
| 261 if (!in_job) | 261 if (!in_job) |
| 262 return true; | 262 return true; |
| 263 | 263 |
| 264 // ...or there is a job but the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit is set. | 264 // ...or there is a job but the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit is set. |
| 265 JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0}; | 265 JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0}; |
| 266 if (!::QueryInformationJobObject(NULL, | 266 if (!::QueryInformationJobObject(nullptr, |
| 267 JobObjectExtendedLimitInformation, &job_info, | 267 JobObjectExtendedLimitInformation, &job_info, |
| 268 sizeof(job_info), NULL)) { | 268 sizeof(job_info), nullptr)) { |
| 269 NOTREACHED() << "QueryInformationJobObject failed. " << GetLastError(); | 269 NOTREACHED() << "QueryInformationJobObject failed. " << GetLastError(); |
| 270 return true; | 270 return true; |
| 271 } | 271 } |
| 272 if (job_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) | 272 if (job_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) |
| 273 return true; | 273 return true; |
| 274 | 274 |
| 275 return false; | 275 return false; |
| 276 } | 276 } |
| 277 | 277 |
| 278 // Adds the generic policy rules to a sandbox TargetPolicy. | 278 // Adds the generic policy rules to a sandbox TargetPolicy. |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 typedef BOOL (WINAPI *DuplicateHandleFunctionPtr)(HANDLE source_process_handle, | 409 typedef BOOL (WINAPI *DuplicateHandleFunctionPtr)(HANDLE source_process_handle, |
| 410 HANDLE source_handle, | 410 HANDLE source_handle, |
| 411 HANDLE target_process_handle, | 411 HANDLE target_process_handle, |
| 412 LPHANDLE target_handle, | 412 LPHANDLE target_handle, |
| 413 DWORD desired_access, | 413 DWORD desired_access, |
| 414 BOOL inherit_handle, | 414 BOOL inherit_handle, |
| 415 DWORD options); | 415 DWORD options); |
| 416 | 416 |
| 417 DuplicateHandleFunctionPtr g_iat_orig_duplicate_handle; | 417 DuplicateHandleFunctionPtr g_iat_orig_duplicate_handle; |
| 418 | 418 |
| 419 NtQueryObject g_QueryObject = NULL; | 419 NtQueryObject g_QueryObject = nullptr; |
| 420 | 420 |
| 421 static const char* kDuplicateHandleWarning = | 421 static const char* kDuplicateHandleWarning = |
| 422 "You are attempting to duplicate a privileged handle into a sandboxed" | 422 "You are attempting to duplicate a privileged handle into a sandboxed" |
| 423 " process.\n Please use the sandbox::BrokerDuplicateHandle API or" | 423 " process.\n Please use the sandbox::BrokerDuplicateHandle API or" |
| 424 " contact security@chromium.org for assistance."; | 424 " contact security@chromium.org for assistance."; |
| 425 | 425 |
| 426 void CheckDuplicateHandle(HANDLE handle) { | 426 void CheckDuplicateHandle(HANDLE handle) { |
| 427 // Get the object type (32 characters is safe; current max is 14). | 427 // Get the object type (32 characters is safe; current max is 14). |
| 428 BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)]; | 428 BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)]; |
| 429 OBJECT_TYPE_INFORMATION* type_info = | 429 OBJECT_TYPE_INFORMATION* type_info = |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 465 desired_access, inherit_handle, options)) | 465 desired_access, inherit_handle, options)) |
| 466 return FALSE; | 466 return FALSE; |
| 467 | 467 |
| 468 // We're not worried about broker handles or not crossing process boundaries. | 468 // We're not worried about broker handles or not crossing process boundaries. |
| 469 if (source_process_handle == target_process_handle || | 469 if (source_process_handle == target_process_handle || |
| 470 target_process_handle == ::GetCurrentProcess()) | 470 target_process_handle == ::GetCurrentProcess()) |
| 471 return TRUE; | 471 return TRUE; |
| 472 | 472 |
| 473 // Only sandboxed children are placed in jobs, so just check them. | 473 // Only sandboxed children are placed in jobs, so just check them. |
| 474 BOOL is_in_job = FALSE; | 474 BOOL is_in_job = FALSE; |
| 475 if (!::IsProcessInJob(target_process_handle, NULL, &is_in_job)) { | 475 if (!::IsProcessInJob(target_process_handle, nullptr, &is_in_job)) { |
| 476 // We need a handle with permission to check the job object. | 476 // We need a handle with permission to check the job object. |
| 477 if (ERROR_ACCESS_DENIED == ::GetLastError()) { | 477 if (ERROR_ACCESS_DENIED == ::GetLastError()) { |
| 478 HANDLE temp_handle; | 478 HANDLE temp_handle; |
| 479 CHECK(g_iat_orig_duplicate_handle(::GetCurrentProcess(), | 479 CHECK(g_iat_orig_duplicate_handle(::GetCurrentProcess(), |
| 480 target_process_handle, | 480 target_process_handle, |
| 481 ::GetCurrentProcess(), | 481 ::GetCurrentProcess(), |
| 482 &temp_handle, | 482 &temp_handle, |
| 483 PROCESS_QUERY_INFORMATION, | 483 PROCESS_QUERY_INFORMATION, |
| 484 FALSE, 0)); | 484 FALSE, 0)); |
| 485 base::win::ScopedHandle process(temp_handle); | 485 base::win::ScopedHandle process(temp_handle); |
| 486 CHECK(::IsProcessInJob(process.Get(), NULL, &is_in_job)); | 486 CHECK(::IsProcessInJob(process.Get(), nullptr, &is_in_job)); |
| 487 } | 487 } |
| 488 } | 488 } |
| 489 | 489 |
| 490 if (is_in_job) { | 490 if (is_in_job) { |
| 491 // We never allow inheritable child handles. | 491 // We never allow inheritable child handles. |
| 492 CHECK(!inherit_handle) << kDuplicateHandleWarning; | 492 CHECK(!inherit_handle) << kDuplicateHandleWarning; |
| 493 | 493 |
| 494 // Duplicate the handle again, to get the final permissions. | 494 // Duplicate the handle again, to get the final permissions. |
| 495 HANDLE temp_handle; | 495 HANDLE temp_handle; |
| 496 CHECK(g_iat_orig_duplicate_handle(target_process_handle, *target_handle, | 496 CHECK(g_iat_orig_duplicate_handle(target_process_handle, *target_handle, |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 535 // TODO(abarth): DCHECK(CalledOnValidThread()); | 535 // TODO(abarth): DCHECK(CalledOnValidThread()); |
| 536 // See <http://b/1287166>. | 536 // See <http://b/1287166>. |
| 537 DCHECK(broker_services); | 537 DCHECK(broker_services); |
| 538 DCHECK(!g_broker_services); | 538 DCHECK(!g_broker_services); |
| 539 sandbox::ResultCode result = broker_services->Init(); | 539 sandbox::ResultCode result = broker_services->Init(); |
| 540 g_broker_services = broker_services; | 540 g_broker_services = broker_services; |
| 541 | 541 |
| 542 // In non-official builds warn about dangerous uses of DuplicateHandle. | 542 // In non-official builds warn about dangerous uses of DuplicateHandle. |
| 543 #ifndef OFFICIAL_BUILD | 543 #ifndef OFFICIAL_BUILD |
| 544 BOOL is_in_job = FALSE; | 544 BOOL is_in_job = FALSE; |
| 545 CHECK(::IsProcessInJob(::GetCurrentProcess(), NULL, &is_in_job)); | 545 CHECK(::IsProcessInJob(::GetCurrentProcess(), nullptr, &is_in_job)); |
| 546 // In a Syzygy-profiled binary, instrumented for import profiling, this | 546 // In a Syzygy-profiled binary, instrumented for import profiling, this |
| 547 // patch will end in infinite recursion on the attempted delegation to the | 547 // patch will end in infinite recursion on the attempted delegation to the |
| 548 // original function. | 548 // original function. |
| 549 if (!base::debug::IsBinaryInstrumented() && | 549 if (!base::debug::IsBinaryInstrumented() && |
| 550 !is_in_job && !g_iat_patch_duplicate_handle.is_patched()) { | 550 !is_in_job && !g_iat_patch_duplicate_handle.is_patched()) { |
| 551 HMODULE module = NULL; | 551 HMODULE module = nullptr; |
| 552 wchar_t module_name[MAX_PATH]; | 552 wchar_t module_name[MAX_PATH]; |
| 553 CHECK(::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 553 CHECK(::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
| 554 reinterpret_cast<LPCWSTR>(InitBrokerServices), | 554 reinterpret_cast<LPCWSTR>(InitBrokerServices), |
| 555 &module)); | 555 &module)); |
| 556 DWORD result = ::GetModuleFileNameW(module, module_name, MAX_PATH); | 556 DWORD result = ::GetModuleFileNameW(module, module_name, MAX_PATH); |
| 557 if (result && (result != MAX_PATH)) { | 557 if (result && (result != MAX_PATH)) { |
| 558 ResolveNTFunctionPtr("NtQueryObject", &g_QueryObject); | 558 ResolveNTFunctionPtr("NtQueryObject", &g_QueryObject); |
| 559 result = g_iat_patch_duplicate_handle.Patch( | 559 result = g_iat_patch_duplicate_handle.Patch( |
| 560 module_name, "kernel32.dll", "DuplicateHandle", | 560 module_name, "kernel32.dll", "DuplicateHandle", |
| 561 DuplicateHandlePatch); | 561 DuplicateHandlePatch); |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 649 sandbox::MITIGATION_DEP | | 649 sandbox::MITIGATION_DEP | |
| 650 sandbox::MITIGATION_DEP_NO_ATL_THUNK | | 650 sandbox::MITIGATION_DEP_NO_ATL_THUNK | |
| 651 sandbox::MITIGATION_SEHOP; | 651 sandbox::MITIGATION_SEHOP; |
| 652 | 652 |
| 653 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && | 653 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && |
| 654 type_str == switches::kRendererProcess && | 654 type_str == switches::kRendererProcess && |
| 655 browser_command_line.HasSwitch( | 655 browser_command_line.HasSwitch( |
| 656 switches::kEnableWin32kRendererLockDown)) { | 656 switches::kEnableWin32kRendererLockDown)) { |
| 657 if (policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN, | 657 if (policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN, |
| 658 sandbox::TargetPolicy::FAKE_USER_GDI_INIT, | 658 sandbox::TargetPolicy::FAKE_USER_GDI_INIT, |
| 659 NULL) != sandbox::SBOX_ALL_OK) { | 659 nullptr) != sandbox::SBOX_ALL_OK) { |
| 660 return 0; | 660 return 0; |
| 661 } | 661 } |
| 662 mitigations |= sandbox::MITIGATION_WIN32K_DISABLE; | 662 mitigations |= sandbox::MITIGATION_WIN32K_DISABLE; |
| 663 } | 663 } |
| 664 | 664 |
| 665 if (policy->SetProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK) | 665 if (policy->SetProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK) |
| 666 return 0; | 666 return 0; |
| 667 | 667 |
| 668 mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS | | 668 mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS | |
| 669 sandbox::MITIGATION_DLL_SEARCH_ORDER; | 669 sandbox::MITIGATION_DLL_SEARCH_ORDER; |
| 670 | 670 |
| 671 if (policy->SetDelayedProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK) | 671 if (policy->SetDelayedProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK) |
| 672 return 0; | 672 return 0; |
| 673 | 673 |
| 674 SetJobLevel(*cmd_line, sandbox::JOB_LOCKDOWN, 0, policy); | 674 SetJobLevel(*cmd_line, sandbox::JOB_LOCKDOWN, 0, policy); |
| 675 | 675 |
| 676 bool disable_default_policy = false; | 676 bool disable_default_policy = false; |
| 677 base::FilePath exposed_dir; | 677 base::FilePath exposed_dir; |
| 678 if (delegate) | 678 if (delegate) |
| 679 delegate->PreSandbox(&disable_default_policy, &exposed_dir); | 679 delegate->PreSandbox(&disable_default_policy, &exposed_dir); |
| 680 | 680 |
| 681 if (!disable_default_policy && !AddPolicyForSandboxedProcess(policy)) | 681 if (!disable_default_policy && !AddPolicyForSandboxedProcess(policy)) |
| 682 return 0; | 682 return 0; |
| 683 | 683 |
| 684 if (type_str == switches::kRendererProcess) { | 684 if (type_str == switches::kRendererProcess) { |
| 685 if (ShouldUseDirectWrite()) { | 685 if (ShouldUseDirectWrite()) { |
| 686 AddDirectory(base::DIR_WINDOWS_FONTS, | 686 AddDirectory(base::DIR_WINDOWS_FONTS, |
| 687 NULL, | 687 nullptr, |
| 688 true, | 688 true, |
| 689 sandbox::TargetPolicy::FILES_ALLOW_READONLY, | 689 sandbox::TargetPolicy::FILES_ALLOW_READONLY, |
| 690 policy); | 690 policy); |
| 691 } | 691 } |
| 692 } else { | 692 } else { |
| 693 // Hack for Google Desktop crash. Trick GD into not injecting its DLL into | 693 // Hack for Google Desktop crash. Trick GD into not injecting its DLL into |
| 694 // this subprocess. See | 694 // this subprocess. See |
| 695 // http://code.google.com/p/chromium/issues/detail?id=25580 | 695 // http://code.google.com/p/chromium/issues/detail?id=25580 |
| 696 cmd_line->AppendSwitchASCII("ignored", " --type=renderer "); | 696 cmd_line->AppendSwitchASCII("ignored", " --type=renderer "); |
| 697 } | 697 } |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 791 } | 791 } |
| 792 | 792 |
| 793 return false; | 793 return false; |
| 794 } | 794 } |
| 795 | 795 |
| 796 bool BrokerAddTargetPeer(HANDLE peer_process) { | 796 bool BrokerAddTargetPeer(HANDLE peer_process) { |
| 797 return g_broker_services->AddTargetPeer(peer_process) == sandbox::SBOX_ALL_OK; | 797 return g_broker_services->AddTargetPeer(peer_process) == sandbox::SBOX_ALL_OK; |
| 798 } | 798 } |
| 799 | 799 |
| 800 } // namespace content | 800 } // namespace content |
| OLD | NEW |