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