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