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 #include "content/common/sandbox_win.h" 6 7 #include <string> 8 9 #include "base/base_switches.h" 10 #include "base/command_line.h" 11 #include "base/debug/profiler.h" 12 #include "base/debug/trace_event.h" 13 #include "base/file_util.h" 14 #include "base/hash.h" 15 #include "base/metrics/field_trial.h" 16 #include "base/path_service.h" 17 #include "base/process/launch.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/win/iat_patch_function.h" 21 #include "base/win/scoped_handle.h" 22 #include "base/win/scoped_process_information.h" 23 #include "base/win/windows_version.h" 24 #include "content/public/common/content_client.h" 25 #include "content/public/common/content_switches.h" 26 #include "content/public/common/sandbox_init.h" 27 #include "content/public/common/sandboxed_process_launcher_delegate.h" 28 #include "sandbox/win/src/process_mitigations.h" 29 #include "sandbox/win/src/sandbox.h" 30 #include "sandbox/win/src/sandbox_nt_util.h" 31 #include "sandbox/win/src/win_utils.h" 32 #include "ui/gfx/win/dpi.h" 33 34 static sandbox::BrokerServices* g_broker_services = NULL; 35 static sandbox::TargetServices* g_target_services = NULL; 36 37 namespace content { 38 namespace { 39 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 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 44 // of it, see: 45 // https://sites.google.com/a/chromium.org/dev/Home/third-party-developers 46 // If the size of this list exceeds 64, change kTroublesomeDllsMaxCount. 47 const wchar_t* const kTroublesomeDlls[] = { 48 L"adialhk.dll", // Kaspersky Internet Security. 49 L"acpiz.dll", // Unknown. 50 L"akinsofthook32.dll", // Akinsoft Software Engineering. 51 L"assistant_x64.dll", // Unknown. 52 L"avcuf64.dll", // Bit Defender Internet Security x64. 53 L"avgrsstx.dll", // AVG 8. 54 L"babylonchromepi.dll", // Babylon translator. 55 L"btkeyind.dll", // Widcomm Bluetooth. 56 L"cmcsyshk.dll", // CMC Internet Security. 57 L"cmsetac.dll", // Unknown (suspected malware). 58 L"cooliris.dll", // CoolIris. 59 L"dockshellhook.dll", // Stardock Objectdock. 60 L"easyhook32.dll", // GDIPP and others. 61 L"googledesktopnetwork3.dll", // Google Desktop Search v5. 62 L"fwhook.dll", // PC Tools Firewall Plus. 63 L"hookprocesscreation.dll", // Blumentals Program protector. 64 L"hookterminateapis.dll", // Blumentals and Cyberprinter. 65 L"hookprintapis.dll", // Cyberprinter. 66 L"imon.dll", // NOD32 Antivirus. 67 L"ioloHL.dll", // Iolo (System Mechanic). 68 L"kloehk.dll", // Kaspersky Internet Security. 69 L"lawenforcer.dll", // Spyware-Browser AntiSpyware (Spybro). 70 L"libdivx.dll", // DivX. 71 L"lvprcinj01.dll", // Logitech QuickCam. 72 L"madchook.dll", // Madshi (generic hooking library). 73 L"mdnsnsp.dll", // Bonjour. 74 L"moonsysh.dll", // Moon Secure Antivirus. 75 L"mpk.dll", // KGB Spy. 76 L"npdivx32.dll", // DivX. 77 L"npggNT.des", // GameGuard 2008. 78 L"npggNT.dll", // GameGuard (older). 79 L"oawatch.dll", // Online Armor. 80 L"pavhook.dll", // Panda Internet Security. 81 L"pavlsphook.dll", // Panda Antivirus. 82 L"pavshook.dll", // Panda Antivirus. 83 L"pavshookwow.dll", // Panda Antivirus. 84 L"pctavhook.dll", // PC Tools Antivirus. 85 L"pctgmhk.dll", // PC Tools Spyware Doctor. 86 L"prntrack.dll", // Pharos Systems. 87 L"protector.dll", // Unknown (suspected malware). 88 L"radhslib.dll", // Radiant Naomi Internet Filter. 89 L"radprlib.dll", // Radiant Naomi Internet Filter. 90 L"rapportnikko.dll", // Trustware Rapport. 91 L"rlhook.dll", // Trustware Bufferzone. 92 L"rooksdol.dll", // Trustware Rapport. 93 L"rndlpepperbrowserrecordhelper.dll", // RealPlayer. 94 L"rpchromebrowserrecordhelper.dll", // RealPlayer. 95 L"r3hook.dll", // Kaspersky Internet Security. 96 L"sahook.dll", // McAfee Site Advisor. 97 L"sbrige.dll", // Unknown. 98 L"sc2hook.dll", // Supercopier 2. 99 L"sdhook32.dll", // Spybot - Search & Destroy Live Protection. 100 L"sguard.dll", // Iolo (System Guard). 101 L"smum32.dll", // Spyware Doctor version 6. 102 L"smumhook.dll", // Spyware Doctor version 5. 103 L"ssldivx.dll", // DivX. 104 L"syncor11.dll", // SynthCore Midi interface. 105 L"systools.dll", // Panda Antivirus. 106 L"tfwah.dll", // Threatfire (PC tools). 107 L"wblind.dll", // Stardock Object desktop. 108 L"wbhelp.dll", // Stardock Object desktop. 109 L"winstylerthemehelper.dll" // Tuneup utilities 2006. 110 }; 111 112 // Adds the policy rules for the path and path\ with the semantic |access|. 113 // If |children| is set to true, we need to add the wildcard rules to also 114 // apply the rule to the subfiles and subfolders. 115 bool AddDirectory(int path, const wchar_t* sub_dir, bool children, 116 sandbox::TargetPolicy::Semantics access, 117 sandbox::TargetPolicy* policy) { 118 base::FilePath directory; 119 if (!PathService::Get(path, &directory)) 120 return false; 121 122 if (sub_dir) 123 directory = base::MakeAbsoluteFilePath(directory.Append(sub_dir)); 124 125 sandbox::ResultCode result; 126 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access, 127 directory.value().c_str()); 128 if (result != sandbox::SBOX_ALL_OK) 129 return false; 130 131 std::wstring directory_str = directory.value() + L"\\"; 132 if (children) 133 directory_str += L"*"; 134 // Otherwise, add the version of the path that ends with a separator. 135 136 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access, 137 directory_str.c_str()); 138 if (result != sandbox::SBOX_ALL_OK) 139 return false; 140 141 return true; 142 } 143 144 // Adds the policy rules for the path and path\* with the semantic |access|. 145 // We need to add the wildcard rules to also apply the rule to the subkeys. 146 bool AddKeyAndSubkeys(std::wstring key, 147 sandbox::TargetPolicy::Semantics access, 148 sandbox::TargetPolicy* policy) { 149 sandbox::ResultCode result; 150 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access, 151 key.c_str()); 152 if (result != sandbox::SBOX_ALL_OK) 153 return false; 154 155 key += L"\\*"; 156 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access, 157 key.c_str()); 158 if (result != sandbox::SBOX_ALL_OK) 159 return false; 160 161 return true; 162 } 163 164 // Compares the loaded |module| file name matches |module_name|. 165 bool IsExpandedModuleName(HMODULE module, const wchar_t* module_name) { 166 wchar_t path[MAX_PATH]; 167 DWORD sz = ::GetModuleFileNameW(module, path, arraysize(path)); 168 if ((sz == arraysize(path)) || (sz == 0)) { 169 // XP does not set the last error properly, so we bail out anyway. 170 return false; 171 } 172 if (!::GetLongPathName(path, path, arraysize(path))) 173 return false; 174 base::FilePath fname(path); 175 return (fname.BaseName().value() == module_name); 176 } 177 178 // Adds a single dll by |module_name| into the |policy| blacklist. 179 // If |check_in_browser| is true we only add an unload policy only if the dll 180 // is also loaded in this process. 181 void BlacklistAddOneDll(const wchar_t* module_name, 182 bool check_in_browser, 183 sandbox::TargetPolicy* policy) { 184 HMODULE module = check_in_browser ? ::GetModuleHandleW(module_name) : NULL; 185 if (!module) { 186 // The module could have been loaded with a 8.3 short name. We check 187 // the three most common cases: 'thelongname.dll' becomes 188 // 'thelon~1.dll', 'thelon~2.dll' and 'thelon~3.dll'. 189 std::wstring name(module_name); 190 size_t period = name.rfind(L'.'); 191 DCHECK_NE(std::string::npos, period); 192 DCHECK_LE(3U, (name.size() - period)); 193 if (period <= 8) 194 return; 195 for (int ix = 0; ix < 3; ++ix) { 196 const wchar_t suffix[] = {'~', ('1' + ix), 0}; 197 std::wstring alt_name = name.substr(0, 6) + suffix; 198 alt_name += name.substr(period, name.size()); 199 if (check_in_browser) { 200 module = ::GetModuleHandleW(alt_name.c_str()); 201 if (!module) 202 return; 203 // We found it, but because it only has 6 significant letters, we 204 // want to make sure it is the right one. 205 if (!IsExpandedModuleName(module, module_name)) 206 return; 207 } 208 // Found a match. We add both forms to the policy. 209 policy->AddDllToUnload(alt_name.c_str()); 210 } 211 } 212 policy->AddDllToUnload(module_name); 213 DVLOG(1) << "dll to unload found: " << module_name; 214 return; 215 } 216 217 // Adds policy rules for unloaded the known dlls that cause chrome to crash. 218 // Eviction of injected DLLs is done by the sandbox so that the injected module 219 // does not get a chance to execute any code. 220 void AddGenericDllEvictionPolicy(sandbox::TargetPolicy* policy) { 221 for (int ix = 0; ix != arraysize(kTroublesomeDlls); ++ix) 222 BlacklistAddOneDll(kTroublesomeDlls[ix], true, policy); 223 } 224 225 // Returns the object path prepended with the current logon session. 226 base::string16 PrependWindowsSessionPath(const base::char16* object) { 227 // Cache this because it can't change after process creation. 228 static uintptr_t s_session_id = 0; 229 if (s_session_id == 0) { 230 HANDLE token; 231 DWORD session_id_length; 232 DWORD session_id = 0; 233 234 CHECK(::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)); 235 CHECK(::GetTokenInformation(token, TokenSessionId, &session_id, 236 sizeof(session_id), &session_id_length)); 237 CloseHandle(token); 238 if (session_id) 239 s_session_id = session_id; 240 } 241 242 return base::StringPrintf(L"\\Sessions\\%d%ls", s_session_id, object); 243 } 244 245 // Checks if the sandbox should be let to run without a job object assigned. 246 bool ShouldSetJobLevel(const CommandLine& cmd_line) { 247 if (!cmd_line.HasSwitch(switches::kAllowNoSandboxJob)) 248 return true; 249 250 // Windows 8 allows nested jobs so we don't need to check if we are in other 251 // job. 252 if (base::win::GetVersion() >= base::win::VERSION_WIN8) 253 return true; 254 255 BOOL in_job = true; 256 // Either there is no job yet associated so we must add our job, 257 if (!::IsProcessInJob(::GetCurrentProcess(), NULL, &in_job)) 258 NOTREACHED() << "IsProcessInJob failed. " << GetLastError(); 259 if (!in_job) 260 return true; 261 262 // ...or there is a job but the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit is set. 263 JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0}; 264 if (!::QueryInformationJobObject(NULL, 265 JobObjectExtendedLimitInformation, &job_info, 266 sizeof(job_info), NULL)) { 267 NOTREACHED() << "QueryInformationJobObject failed. " << GetLastError(); 268 return true; 269 } 270 if (job_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) 271 return true; 272 273 return false; 274 } 275 276 // Adds the generic policy rules to a sandbox TargetPolicy. 277 bool AddGenericPolicy(sandbox::TargetPolicy* policy) { 278 sandbox::ResultCode result; 279 280 // Renderers need to copy sections for plugin DIBs and GPU. 281 // GPU needs to copy sections to renderers. 282 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES, 283 sandbox::TargetPolicy::HANDLES_DUP_ANY, 284 L"Section"); 285 if (result != sandbox::SBOX_ALL_OK) 286 return false; 287 288 // Add the policy for the client side of a pipe. It is just a file 289 // in the \pipe\ namespace. We restrict it to pipes that start with 290 // "chrome." so the sandboxed process cannot connect to system services. 291 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, 292 sandbox::TargetPolicy::FILES_ALLOW_ANY, 293 L"\\??\\pipe\\chrome.*"); 294 if (result != sandbox::SBOX_ALL_OK) 295 return false; 296 297 // Add the policy for the server side of nacl pipe. It is just a file 298 // in the \pipe\ namespace. We restrict it to pipes that start with 299 // "chrome.nacl" so the sandboxed process cannot connect to 300 // system services. 301 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES, 302 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, 303 L"\\\\.\\pipe\\chrome.nacl.*"); 304 if (result != sandbox::SBOX_ALL_OK) 305 return false; 306 307 // Allow the server side of sync sockets, which are pipes that have 308 // the "chrome.sync" namespace and a randomly generated suffix. 309 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES, 310 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, 311 L"\\\\.\\pipe\\chrome.sync.*"); 312 if (result != sandbox::SBOX_ALL_OK) 313 return false; 314 315 // Add the policy for debug message only in debug 316 #ifndef NDEBUG 317 base::FilePath app_dir; 318 if (!PathService::Get(base::DIR_MODULE, &app_dir)) 319 return false; 320 321 wchar_t long_path_buf[MAX_PATH]; 322 DWORD long_path_return_value = GetLongPathName(app_dir.value().c_str(), 323 long_path_buf, 324 MAX_PATH); 325 if (long_path_return_value == 0 || long_path_return_value >= MAX_PATH) 326 return false; 327 328 base::FilePath debug_message(long_path_buf); 329 debug_message = debug_message.AppendASCII("debug_message.exe"); 330 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_PROCESS, 331 sandbox::TargetPolicy::PROCESS_MIN_EXEC, 332 debug_message.value().c_str()); 333 if (result != sandbox::SBOX_ALL_OK) 334 return false; 335 #endif // NDEBUG 336 337 AddGenericDllEvictionPolicy(policy); 338 return true; 339 } 340 341 bool AddPolicyForSandboxedProcess(sandbox::TargetPolicy* policy) { 342 sandbox::ResultCode result; 343 // Renderers need to share events with plugins. 344 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES, 345 sandbox::TargetPolicy::HANDLES_DUP_ANY, 346 L"Event"); 347 if (result != sandbox::SBOX_ALL_OK) 348 return false; 349 350 // Win8+ adds a device DeviceApi that we don't need. 351 if (base::win::GetVersion() > base::win::VERSION_WIN7) 352 policy->AddKernelObjectToClose(L"File", L"\\Device\\DeviceApi"); 353 354 sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED; 355 if (base::win::GetVersion() > base::win::VERSION_XP) { 356 // On 2003/Vista the initial token has to be restricted if the main 357 // token is restricted. 358 initial_token = sandbox::USER_RESTRICTED_SAME_ACCESS; 359 } 360 361 policy->SetTokenLevel(initial_token, sandbox::USER_LOCKDOWN); 362 // Prevents the renderers from manipulating low-integrity processes. 363 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED); 364 365 if (sandbox::SBOX_ALL_OK != policy->SetAlternateDesktop(true)) { 366 DLOG(WARNING) << "Failed to apply desktop security to the renderer"; 367 } 368 369 return true; 370 } 371 372 // Updates the command line arguments with debug-related flags. If debug flags 373 // have been used with this process, they will be filtered and added to 374 // command_line as needed. 375 void ProcessDebugFlags(CommandLine* command_line) { 376 const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess(); 377 std::string type = command_line->GetSwitchValueASCII(switches::kProcessType); 378 if (current_cmd_line.HasSwitch(switches::kWaitForDebuggerChildren)) { 379 // Look to pass-on the kWaitForDebugger flag. 380 std::string value = current_cmd_line.GetSwitchValueASCII( 381 switches::kWaitForDebuggerChildren); 382 if (value.empty() || value == type) { 383 command_line->AppendSwitch(switches::kWaitForDebugger); 384 } 385 command_line->AppendSwitchASCII(switches::kWaitForDebuggerChildren, value); 386 } 387 } 388 389 // This code is test only, and attempts to catch unsafe uses of 390 // DuplicateHandle() that copy privileged handles into sandboxed processes. 391 #ifndef OFFICIAL_BUILD 392 base::win::IATPatchFunction g_iat_patch_duplicate_handle; 393 394 typedef BOOL (WINAPI *DuplicateHandleFunctionPtr)(HANDLE source_process_handle, 395 HANDLE source_handle, 396 HANDLE target_process_handle, 397 LPHANDLE target_handle, 398 DWORD desired_access, 399 BOOL inherit_handle, 400 DWORD options); 401 402 DuplicateHandleFunctionPtr g_iat_orig_duplicate_handle; 403 404 NtQueryObject g_QueryObject = NULL; 405 406 static const char* kDuplicateHandleWarning = 407 "You are attempting to duplicate a privileged handle into a sandboxed" 408 " process.\n Please use the sandbox::BrokerDuplicateHandle API or" 409 " contact security (at) chromium.org for assistance."; 410 411 void CheckDuplicateHandle(HANDLE handle) { 412 // Get the object type (32 characters is safe; current max is 14). 413 BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)]; 414 OBJECT_TYPE_INFORMATION* type_info = 415 reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer); 416 ULONG size = sizeof(buffer) - sizeof(wchar_t); 417 NTSTATUS error; 418 error = g_QueryObject(handle, ObjectTypeInformation, type_info, size, &size); 419 CHECK(NT_SUCCESS(error)); 420 type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; 421 422 // Get the object basic information. 423 OBJECT_BASIC_INFORMATION basic_info; 424 size = sizeof(basic_info); 425 error = g_QueryObject(handle, ObjectBasicInformation, &basic_info, size, 426 &size); 427 CHECK(NT_SUCCESS(error)); 428 429 CHECK(!(basic_info.GrantedAccess & WRITE_DAC)) << 430 kDuplicateHandleWarning; 431 432 if (0 == _wcsicmp(type_info->Name.Buffer, L"Process")) { 433 const ACCESS_MASK kDangerousMask = ~(PROCESS_QUERY_LIMITED_INFORMATION | 434 SYNCHRONIZE); 435 CHECK(!(basic_info.GrantedAccess & kDangerousMask)) << 436 kDuplicateHandleWarning; 437 } 438 } 439 440 BOOL WINAPI DuplicateHandlePatch(HANDLE source_process_handle, 441 HANDLE source_handle, 442 HANDLE target_process_handle, 443 LPHANDLE target_handle, 444 DWORD desired_access, 445 BOOL inherit_handle, 446 DWORD options) { 447 // Duplicate the handle so we get the final access mask. 448 if (!g_iat_orig_duplicate_handle(source_process_handle, source_handle, 449 target_process_handle, target_handle, 450 desired_access, inherit_handle, options)) 451 return FALSE; 452 453 // We're not worried about broker handles or not crossing process boundaries. 454 if (source_process_handle == target_process_handle || 455 target_process_handle == ::GetCurrentProcess()) 456 return TRUE; 457 458 // Only sandboxed children are placed in jobs, so just check them. 459 BOOL is_in_job = FALSE; 460 if (!::IsProcessInJob(target_process_handle, NULL, &is_in_job)) { 461 // We need a handle with permission to check the job object. 462 if (ERROR_ACCESS_DENIED == ::GetLastError()) { 463 HANDLE temp_handle; 464 CHECK(g_iat_orig_duplicate_handle(::GetCurrentProcess(), 465 target_process_handle, 466 ::GetCurrentProcess(), 467 &temp_handle, 468 PROCESS_QUERY_INFORMATION, 469 FALSE, 0)); 470 base::win::ScopedHandle process(temp_handle); 471 CHECK(::IsProcessInJob(process, NULL, &is_in_job)); 472 } 473 } 474 475 if (is_in_job) { 476 // We never allow inheritable child handles. 477 CHECK(!inherit_handle) << kDuplicateHandleWarning; 478 479 // Duplicate the handle again, to get the final permissions. 480 HANDLE temp_handle; 481 CHECK(g_iat_orig_duplicate_handle(target_process_handle, *target_handle, 482 ::GetCurrentProcess(), &temp_handle, 483 0, FALSE, DUPLICATE_SAME_ACCESS)); 484 base::win::ScopedHandle handle(temp_handle); 485 486 // Callers use CHECK macro to make sure we get the right stack. 487 CheckDuplicateHandle(handle); 488 } 489 490 return TRUE; 491 } 492 #endif 493 494 } // namespace 495 496 void SetJobLevel(const CommandLine& cmd_line, 497 sandbox::JobLevel job_level, 498 uint32 ui_exceptions, 499 sandbox::TargetPolicy* policy) { 500 if (ShouldSetJobLevel(cmd_line)) { 501 #ifdef _WIN64 502 policy->SetJobMemoryLimit(4ULL * 1024 * 1024 * 1024); 503 #endif 504 policy->SetJobLevel(job_level, ui_exceptions); 505 } else { 506 policy->SetJobLevel(sandbox::JOB_NONE, 0); 507 } 508 } 509 510 // TODO(jschuh): Need get these restrictions applied to NaCl and Pepper. 511 // Just have to figure out what needs to be warmed up first. 512 void AddBaseHandleClosePolicy(sandbox::TargetPolicy* policy) { 513 // TODO(cpu): Add back the BaseNamedObjects policy. 514 base::string16 object_path = PrependWindowsSessionPath( 515 L"\\BaseNamedObjects\\windows_shell_global_counters"); 516 policy->AddKernelObjectToClose(L"Section", object_path.data()); 517 } 518 519 bool InitBrokerServices(sandbox::BrokerServices* broker_services) { 520 // TODO(abarth): DCHECK(CalledOnValidThread()); 521 // See <http://b/1287166>. 522 DCHECK(broker_services); 523 DCHECK(!g_broker_services); 524 sandbox::ResultCode result = broker_services->Init(); 525 g_broker_services = broker_services; 526 527 // In non-official builds warn about dangerous uses of DuplicateHandle. 528 #ifndef OFFICIAL_BUILD 529 BOOL is_in_job = FALSE; 530 CHECK(::IsProcessInJob(::GetCurrentProcess(), NULL, &is_in_job)); 531 // In a Syzygy-profiled binary, instrumented for import profiling, this 532 // patch will end in infinite recursion on the attempted delegation to the 533 // original function. 534 if (!base::debug::IsBinaryInstrumented() && 535 !is_in_job && !g_iat_patch_duplicate_handle.is_patched()) { 536 HMODULE module = NULL; 537 wchar_t module_name[MAX_PATH]; 538 CHECK(::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, 539 reinterpret_cast<LPCWSTR>(InitBrokerServices), 540 &module)); 541 DWORD result = ::GetModuleFileNameW(module, module_name, MAX_PATH); 542 if (result && (result != MAX_PATH)) { 543 ResolveNTFunctionPtr("NtQueryObject", &g_QueryObject); 544 result = g_iat_patch_duplicate_handle.Patch( 545 module_name, "kernel32.dll", "DuplicateHandle", 546 DuplicateHandlePatch); 547 CHECK(result == 0); 548 g_iat_orig_duplicate_handle = 549 reinterpret_cast<DuplicateHandleFunctionPtr>( 550 g_iat_patch_duplicate_handle.original_function()); 551 } 552 } 553 #endif 554 555 return sandbox::SBOX_ALL_OK == result; 556 } 557 558 bool InitTargetServices(sandbox::TargetServices* target_services) { 559 DCHECK(target_services); 560 DCHECK(!g_target_services); 561 sandbox::ResultCode result = target_services->Init(); 562 g_target_services = target_services; 563 return sandbox::SBOX_ALL_OK == result; 564 } 565 566 bool ShouldUseDirectWrite() { 567 // If the flag is currently on, and we're on Win7 or above, we enable 568 // DirectWrite. Skia does not require the additions to DirectWrite in QFE 569 // 2670838, but a simple 'better than XP' check is not enough. 570 if (base::win::GetVersion() < base::win::VERSION_WIN7) 571 return false; 572 573 base::win::OSInfo::VersionNumber os_version = 574 base::win::OSInfo::GetInstance()->version_number(); 575 if ((os_version.major == 6) && (os_version.minor == 1)) { 576 // We can't use DirectWrite for pre-release versions of Windows 7. 577 if (os_version.build < 7600) 578 return false; 579 } 580 581 // If forced off, don't use it. 582 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 583 if (command_line.HasSwitch(switches::kDisableDirectWrite)) 584 return false; 585 586 #if !defined(NACL_WIN64) 587 // Can't use GDI on HiDPI. 588 if (gfx::GetDPIScale() > 1.0f) 589 return true; 590 #endif 591 592 // Otherwise, check the field trial. 593 const std::string group_name = 594 base::FieldTrialList::FindFullName("DirectWrite"); 595 return group_name != "Disabled"; 596 } 597 598 base::ProcessHandle StartSandboxedProcess( 599 SandboxedProcessLauncherDelegate* delegate, 600 CommandLine* cmd_line) { 601 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 602 std::string type_str = cmd_line->GetSwitchValueASCII(switches::kProcessType); 603 604 TRACE_EVENT_BEGIN_ETW("StartProcessWithAccess", 0, type_str); 605 606 // Propagate the --allow-no-job flag if present. 607 if (browser_command_line.HasSwitch(switches::kAllowNoSandboxJob) && 608 !cmd_line->HasSwitch(switches::kAllowNoSandboxJob)) { 609 cmd_line->AppendSwitch(switches::kAllowNoSandboxJob); 610 } 611 612 ProcessDebugFlags(cmd_line); 613 614 // Prefetch hints on windows: 615 // Using a different prefetch profile per process type will allow Windows 616 // to create separate pretetch settings for browser, renderer etc. 617 cmd_line->AppendArg(base::StringPrintf("/prefetch:%d", base::Hash(type_str))); 618 619 if ((delegate && !delegate->ShouldSandbox()) || 620 browser_command_line.HasSwitch(switches::kNoSandbox) || 621 cmd_line->HasSwitch(switches::kNoSandbox)) { 622 base::ProcessHandle process = 0; 623 base::LaunchProcess(*cmd_line, base::LaunchOptions(), &process); 624 g_broker_services->AddTargetPeer(process); 625 return process; 626 } 627 628 sandbox::TargetPolicy* policy = g_broker_services->CreatePolicy(); 629 630 sandbox::MitigationFlags mitigations = sandbox::MITIGATION_HEAP_TERMINATE | 631 sandbox::MITIGATION_BOTTOM_UP_ASLR | 632 sandbox::MITIGATION_DEP | 633 sandbox::MITIGATION_DEP_NO_ATL_THUNK | 634 sandbox::MITIGATION_SEHOP; 635 636 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && 637 type_str == switches::kRendererProcess && 638 browser_command_line.HasSwitch( 639 switches::kEnableWin32kRendererLockDown)) { 640 if (policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN, 641 sandbox::TargetPolicy::FAKE_USER_GDI_INIT, 642 NULL) != sandbox::SBOX_ALL_OK) { 643 return 0; 644 } 645 mitigations |= sandbox::MITIGATION_WIN32K_DISABLE; 646 } 647 648 if (policy->SetProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK) 649 return 0; 650 651 mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS | 652 sandbox::MITIGATION_DLL_SEARCH_ORDER; 653 654 if (policy->SetDelayedProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK) 655 return 0; 656 657 SetJobLevel(*cmd_line, sandbox::JOB_LOCKDOWN, 0, policy); 658 659 bool disable_default_policy = false; 660 base::FilePath exposed_dir; 661 if (delegate) 662 delegate->PreSandbox(&disable_default_policy, &exposed_dir); 663 664 if (!disable_default_policy && !AddPolicyForSandboxedProcess(policy)) 665 return 0; 666 667 if (type_str == switches::kRendererProcess) { 668 if (ShouldUseDirectWrite()) { 669 AddDirectory(base::DIR_WINDOWS_FONTS, 670 NULL, 671 true, 672 sandbox::TargetPolicy::FILES_ALLOW_READONLY, 673 policy); 674 } 675 } else { 676 // Hack for Google Desktop crash. Trick GD into not injecting its DLL into 677 // this subprocess. See 678 // http://code.google.com/p/chromium/issues/detail?id=25580 679 cmd_line->AppendSwitchASCII("ignored", " --type=renderer "); 680 } 681 682 sandbox::ResultCode result; 683 if (!exposed_dir.empty()) { 684 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, 685 sandbox::TargetPolicy::FILES_ALLOW_ANY, 686 exposed_dir.value().c_str()); 687 if (result != sandbox::SBOX_ALL_OK) 688 return 0; 689 690 base::FilePath exposed_files = exposed_dir.AppendASCII("*"); 691 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, 692 sandbox::TargetPolicy::FILES_ALLOW_ANY, 693 exposed_files.value().c_str()); 694 if (result != sandbox::SBOX_ALL_OK) 695 return 0; 696 } 697 698 if (!AddGenericPolicy(policy)) { 699 NOTREACHED(); 700 return 0; 701 } 702 703 if (browser_command_line.HasSwitch(switches::kEnableLogging)) { 704 // If stdout/stderr point to a Windows console, these calls will 705 // have no effect. 706 policy->SetStdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE)); 707 policy->SetStderrHandle(GetStdHandle(STD_ERROR_HANDLE)); 708 } 709 710 if (delegate) { 711 bool success = true; 712 delegate->PreSpawnTarget(policy, &success); 713 if (!success) 714 return 0; 715 } 716 717 TRACE_EVENT_BEGIN_ETW("StartProcessWithAccess::LAUNCHPROCESS", 0, 0); 718 719 PROCESS_INFORMATION temp_process_info = {}; 720 result = g_broker_services->SpawnTarget( 721 cmd_line->GetProgram().value().c_str(), 722 cmd_line->GetCommandLineString().c_str(), 723 policy, &temp_process_info); 724 policy->Release(); 725 base::win::ScopedProcessInformation target(temp_process_info); 726 727 TRACE_EVENT_END_ETW("StartProcessWithAccess::LAUNCHPROCESS", 0, 0); 728 729 if (sandbox::SBOX_ALL_OK != result) { 730 if (result == sandbox::SBOX_ERROR_GENERIC) 731 DPLOG(ERROR) << "Failed to launch process"; 732 else 733 DLOG(ERROR) << "Failed to launch process. Error: " << result; 734 return 0; 735 } 736 737 if (delegate) 738 delegate->PostSpawnTarget(target.process_handle()); 739 740 ResumeThread(target.thread_handle()); 741 return target.TakeProcessHandle(); 742 } 743 744 bool BrokerDuplicateHandle(HANDLE source_handle, 745 DWORD target_process_id, 746 HANDLE* target_handle, 747 DWORD desired_access, 748 DWORD options) { 749 // If our process is the target just duplicate the handle. 750 if (::GetCurrentProcessId() == target_process_id) { 751 return !!::DuplicateHandle(::GetCurrentProcess(), source_handle, 752 ::GetCurrentProcess(), target_handle, 753 desired_access, FALSE, options); 754 755 } 756 757 // Try the broker next 758 if (g_target_services && 759 g_target_services->DuplicateHandle(source_handle, target_process_id, 760 target_handle, desired_access, 761 options) == sandbox::SBOX_ALL_OK) { 762 return true; 763 } 764 765 // Finally, see if we already have access to the process. 766 base::win::ScopedHandle target_process; 767 target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, 768 target_process_id)); 769 if (target_process.IsValid()) { 770 return !!::DuplicateHandle(::GetCurrentProcess(), source_handle, 771 target_process, target_handle, 772 desired_access, FALSE, options); 773 } 774 775 return false; 776 } 777 778 bool BrokerAddTargetPeer(HANDLE peer_process) { 779 return g_broker_services->AddTargetPeer(peer_process) == sandbox::SBOX_ALL_OK; 780 } 781 782 } // namespace content 783