sandbox_policy.cc revision 06741cbc25cd4227a9fba40dfd0273bfcc1a587a
1// Copyright (c) 2010 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 "chrome/common/sandbox_policy.h" 6 7#include <string> 8 9#include "app/win_util.h" 10#include "base/command_line.h" 11#include "base/debug_util.h" 12#include "base/file_util.h" 13#include "base/logging.h" 14#include "base/path_service.h" 15#include "base/process_util.h" 16#include "base/registry.h" 17#include "base/string_util.h" 18#include "base/win_util.h" 19#include "chrome/common/child_process_info.h" 20#include "chrome/common/chrome_constants.h" 21#include "chrome/common/chrome_paths.h" 22#include "chrome/common/chrome_switches.h" 23#include "chrome/common/debug_flags.h" 24#include "sandbox/src/sandbox.h" 25 26static sandbox::BrokerServices* g_broker_services = NULL; 27 28namespace { 29 30// The DLLs listed here are known (or under strong suspicion) of causing crashes 31// when they are loaded in the renderer. 32const wchar_t* const kTroublesomeDlls[] = { 33 L"adialhk.dll", // Kaspersky Internet Security. 34 L"acpiz.dll", // Unknown. 35 L"avgrsstx.dll", // AVG 8. 36 L"btkeyind.dll", // Widcomm Bluetooth. 37 L"cmcsyshk.dll", // CMC Internet Security. 38 L"dockshellhook.dll", // Stardock Objectdock. 39 L"GoogleDesktopNetwork3.DLL", // Google Desktop Search v5. 40 L"fwhook.dll", // PC Tools Firewall Plus. 41 L"hookprocesscreation.dll", // Blumentals Program protector. 42 L"hookterminateapis.dll", // Blumentals and Cyberprinter. 43 L"hookprintapis.dll", // Cyberprinter. 44 L"imon.dll", // NOD32 Antivirus. 45 L"ioloHL.dll", // Iolo (System Mechanic). 46 L"kloehk.dll", // Kaspersky Internet Security. 47 L"lawenforcer.dll", // Spyware-Browser AntiSpyware (Spybro). 48 L"libdivx.dll", // DivX. 49 L"lvprcinj01.dll", // Logitech QuickCam. 50 L"madchook.dll", // Madshi (generic hooking library). 51 L"mdnsnsp.dll", // Bonjour. 52 L"moonsysh.dll", // Moon Secure Antivirus. 53 L"npdivx32.dll", // DivX. 54 L"npggNT.des", // GameGuard 2008. 55 L"npggNT.dll", // GameGuard (older). 56 L"oawatch.dll", // Online Armor. 57 L"pavhook.dll", // Panda Internet Security. 58 L"pavshook.dll", // Panda Antivirus. 59 L"pctavhook.dll", // PC Tools Antivirus. 60 L"pctgmhk.dll", // PC Tools Spyware Doctor. 61 L"prntrack.dll", // Pharos Systems. 62 L"radhslib.dll", // Radiant Naomi Internet Filter. 63 L"radprlib.dll", // Radiant Naomi Internet Filter. 64 L"rlhook.dll", // Trustware Bufferzone. 65 L"r3hook.dll", // Kaspersky Internet Security. 66 L"sahook.dll", // McAfee Site Advisor. 67 L"sbrige.dll", // Unknown. 68 L"sc2hook.dll", // Supercopier 2. 69 L"sguard.dll", // Iolo (System Guard). 70 L"smum32.dll", // Spyware Doctor version 6. 71 L"smumhook.dll", // Spyware Doctor version 5. 72 L"ssldivx.dll", // DivX. 73 L"syncor11.dll", // SynthCore Midi interface. 74 L"systools.dll", // Panda Antivirus. 75 L"tfwah.dll", // Threatfire (PC tools). 76 L"wblind.dll", // Stardock Object desktop. 77 L"wbhelp.dll", // Stardock Object desktop. 78 L"winstylerthemehelper.dll" // Tuneup utilities 2006. 79}; 80 81enum PluginPolicyCategory { 82 PLUGIN_GROUP_TRUSTED, 83 PLUGIN_GROUP_UNTRUSTED, 84}; 85 86// Returns the policy category for the plugin dll. 87PluginPolicyCategory GetPolicyCategoryForPlugin( 88 const std::wstring& dll, 89 const std::wstring& list) { 90 std::wstring filename = FilePath(dll).BaseName().value(); 91 std::wstring plugin_dll = StringToLowerASCII(filename); 92 std::wstring trusted_plugins = StringToLowerASCII(list); 93 94 size_t pos = 0; 95 size_t end_item = 0; 96 while (end_item != std::wstring::npos) { 97 end_item = list.find(L",", pos); 98 99 size_t size_item = (end_item == std::wstring::npos) ? end_item : 100 end_item - pos; 101 std::wstring item = list.substr(pos, size_item); 102 if (!item.empty() && item == plugin_dll) 103 return PLUGIN_GROUP_TRUSTED; 104 105 pos = end_item + 1; 106 } 107 108 return PLUGIN_GROUP_UNTRUSTED; 109} 110 111// Adds the policy rules for the path and path\ with the semantic |access|. 112// If |children| is set to true, we need to add the wildcard rules to also 113// apply the rule to the subfiles and subfolders. 114bool AddDirectory(int path, const wchar_t* sub_dir, bool children, 115 sandbox::TargetPolicy::Semantics access, 116 sandbox::TargetPolicy* policy) { 117 std::wstring directory; 118 if (!PathService::Get(path, &directory)) 119 return false; 120 121 if (sub_dir) { 122 file_util::AppendToPath(&directory, sub_dir); 123 file_util::AbsolutePath(&directory); 124 } 125 126 sandbox::ResultCode result; 127 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access, 128 directory.c_str()); 129 if (result != sandbox::SBOX_ALL_OK) 130 return false; 131 132 if (children) 133 file_util::AppendToPath(&directory, L"*"); 134 else 135 // Add the version of the path that ends with a separator. 136 file_util::AppendToPath(&directory, L""); 137 138 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access, 139 directory.c_str()); 140 if (result != sandbox::SBOX_ALL_OK) 141 return false; 142 143 return true; 144} 145 146// Adds the policy rules for the path and path\* with the semantic |access|. 147// We need to add the wildcard rules to also apply the rule to the subkeys. 148bool AddKeyAndSubkeys(std::wstring key, 149 sandbox::TargetPolicy::Semantics access, 150 sandbox::TargetPolicy* policy) { 151 sandbox::ResultCode result; 152 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access, 153 key.c_str()); 154 if (result != sandbox::SBOX_ALL_OK) 155 return false; 156 157 key += L"\\*"; 158 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access, 159 key.c_str()); 160 if (result != sandbox::SBOX_ALL_OK) 161 return false; 162 163 return true; 164} 165 166// Adds policy rules for unloaded the known dlls that cause chrome to crash. 167// Eviction of injected DLLs is done by the sandbox so that the injected module 168// does not get a chance to execute any code. 169void AddDllEvictionPolicy(sandbox::TargetPolicy* policy) { 170 for (int ix = 0; ix != arraysize(kTroublesomeDlls); ++ix) { 171 // To minimize the list we only add an unload policy if the dll is also 172 // loaded in this process. All the injected dlls of interest do this. 173 if (::GetModuleHandleW(kTroublesomeDlls[ix])) { 174 LOG(INFO) << "dll to unload found: " << kTroublesomeDlls[ix]; 175 policy->AddDllToUnload(kTroublesomeDlls[ix]); 176 } 177 } 178} 179 180// Adds the generic policy rules to a sandbox TargetPolicy. 181bool AddGenericPolicy(sandbox::TargetPolicy* policy) { 182 sandbox::ResultCode result; 183 184 // Add the policy for the pipes 185 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, 186 sandbox::TargetPolicy::FILES_ALLOW_ANY, 187 L"\\??\\pipe\\chrome.*"); 188 if (result != sandbox::SBOX_ALL_OK) 189 return false; 190 191 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES, 192 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, 193 L"\\\\.\\pipe\\chrome.nacl.*"); 194 if (result != sandbox::SBOX_ALL_OK) 195 return false; 196 197 // Add the policy for debug message only in debug 198#ifndef NDEBUG 199 std::wstring debug_message; 200 if (!PathService::Get(chrome::DIR_APP, &debug_message)) 201 return false; 202 if (!win_util::ConvertToLongPath(debug_message, &debug_message)) 203 return false; 204 file_util::AppendToPath(&debug_message, L"debug_message.exe"); 205 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_PROCESS, 206 sandbox::TargetPolicy::PROCESS_MIN_EXEC, 207 debug_message.c_str()); 208 if (result != sandbox::SBOX_ALL_OK) 209 return false; 210#endif // NDEBUG 211 212 return true; 213} 214 215// Creates a sandbox without any restriction. 216bool ApplyPolicyForTrustedPlugin(sandbox::TargetPolicy* policy) { 217 policy->SetJobLevel(sandbox::JOB_UNPROTECTED, 0); 218 policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED); 219 return true; 220} 221 222// Creates a sandbox with the plugin running in a restricted environment. 223// Only the "Users" and "Everyone" groups are enabled in the token. The User SID 224// is disabled. 225bool ApplyPolicyForUntrustedPlugin(sandbox::TargetPolicy* policy) { 226 policy->SetJobLevel(sandbox::JOB_UNPROTECTED, 0); 227 228 sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED; 229 if (win_util::GetWinVersion() > win_util::WINVERSION_XP) { 230 // On 2003/Vista the initial token has to be restricted if the main token 231 // is restricted. 232 initial_token = sandbox::USER_RESTRICTED_SAME_ACCESS; 233 } 234 policy->SetTokenLevel(initial_token, sandbox::USER_LIMITED); 235 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); 236 237 if (!AddDirectory(base::DIR_TEMP, NULL, true, 238 sandbox::TargetPolicy::FILES_ALLOW_ANY, policy)) 239 return false; 240 241 if (!AddDirectory(base::DIR_IE_INTERNET_CACHE, NULL, true, 242 sandbox::TargetPolicy::FILES_ALLOW_ANY, policy)) 243 return false; 244 245 if (!AddDirectory(base::DIR_APP_DATA, NULL, true, 246 sandbox::TargetPolicy::FILES_ALLOW_READONLY, 247 policy)) 248 return false; 249 250 if (!AddDirectory(base::DIR_PROFILE, NULL, false, /*not recursive*/ 251 sandbox::TargetPolicy::FILES_ALLOW_READONLY, 252 policy)) 253 return false; 254 255 if (!AddDirectory(base::DIR_APP_DATA, L"Adobe", true, 256 sandbox::TargetPolicy::FILES_ALLOW_ANY, 257 policy)) 258 return false; 259 260 if (!AddDirectory(base::DIR_APP_DATA, L"Macromedia", true, 261 sandbox::TargetPolicy::FILES_ALLOW_ANY, 262 policy)) 263 return false; 264 265 if (!AddDirectory(base::DIR_LOCAL_APP_DATA, NULL, true, 266 sandbox::TargetPolicy::FILES_ALLOW_READONLY, 267 policy)) 268 return false; 269 270 if (!AddKeyAndSubkeys(L"HKEY_CURRENT_USER\\SOFTWARE\\ADOBE", 271 sandbox::TargetPolicy::REG_ALLOW_ANY, 272 policy)) 273 return false; 274 275 if (!AddKeyAndSubkeys(L"HKEY_CURRENT_USER\\SOFTWARE\\MACROMEDIA", 276 sandbox::TargetPolicy::REG_ALLOW_ANY, 277 policy)) 278 return false; 279 280 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) { 281 if (!AddKeyAndSubkeys(L"HKEY_CURRENT_USER\\SOFTWARE\\AppDataLow", 282 sandbox::TargetPolicy::REG_ALLOW_ANY, 283 policy)) 284 return false; 285 286 if (!AddDirectory(base::DIR_LOCAL_APP_DATA_LOW, NULL, true, 287 sandbox::TargetPolicy::FILES_ALLOW_ANY, 288 policy)) 289 return false; 290 291 // DIR_APP_DATA is AppData\Roaming, but Adobe needs to do a directory 292 // listing in AppData directly, so we add a non-recursive policy for 293 // AppData itself. 294 if (!AddDirectory(base::DIR_APP_DATA, L"..", false, 295 sandbox::TargetPolicy::FILES_ALLOW_READONLY, 296 policy)) 297 return false; 298 } 299 300 return true; 301} 302 303// Adds the custom policy rules for a given plugin. |trusted_plugins| contains 304// the comma separate list of plugin dll names that should not be sandboxed. 305bool AddPolicyForPlugin(const CommandLine* cmd_line, 306 sandbox::TargetPolicy* policy) { 307 std::wstring plugin_dll = cmd_line-> 308 GetSwitchValue(switches::kPluginPath); 309 std::wstring trusted_plugins = CommandLine::ForCurrentProcess()-> 310 GetSwitchValue(switches::kTrustedPlugins); 311 // Add the policy for the pipes. 312 sandbox::ResultCode result = sandbox::SBOX_ALL_OK; 313 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES, 314 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, 315 L"\\\\.\\pipe\\chrome.*"); 316 if (result != sandbox::SBOX_ALL_OK) { 317 NOTREACHED(); 318 return false; 319 } 320 321 PluginPolicyCategory policy_category = 322 GetPolicyCategoryForPlugin(plugin_dll, trusted_plugins); 323 324 switch (policy_category) { 325 case PLUGIN_GROUP_TRUSTED: 326 return ApplyPolicyForTrustedPlugin(policy); 327 case PLUGIN_GROUP_UNTRUSTED: 328 return ApplyPolicyForUntrustedPlugin(policy); 329 default: 330 NOTREACHED(); 331 break; 332 } 333 334 return false; 335} 336 337void AddPolicyForRenderer(sandbox::TargetPolicy* policy, 338 bool* on_sandbox_desktop) { 339 policy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0); 340 341 sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED; 342 if (win_util::GetWinVersion() > win_util::WINVERSION_XP) { 343 // On 2003/Vista the initial token has to be restricted if the main 344 // token is restricted. 345 initial_token = sandbox::USER_RESTRICTED_SAME_ACCESS; 346 } 347 348 policy->SetTokenLevel(initial_token, sandbox::USER_LOCKDOWN); 349 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); 350 351 bool use_winsta = !CommandLine::ForCurrentProcess()->HasSwitch( 352 switches::kDisableAltWinstation); 353 354 if (sandbox::SBOX_ALL_OK == policy->SetAlternateDesktop(use_winsta)) { 355 *on_sandbox_desktop = true; 356 } else { 357 *on_sandbox_desktop = false; 358 DLOG(WARNING) << "Failed to apply desktop security to the renderer"; 359 } 360 361 AddDllEvictionPolicy(policy); 362} 363 364} // namespace 365 366namespace sandbox { 367 368void InitBrokerServices(sandbox::BrokerServices* broker_services) { 369 // TODO(abarth): DCHECK(CalledOnValidThread()); 370 // See <http://b/1287166>. 371 CHECK(broker_services); 372 CHECK(!g_broker_services); 373 broker_services->Init(); 374 g_broker_services = broker_services; 375} 376 377base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, 378 const FilePath& exposed_dir) { 379 base::ProcessHandle process = 0; 380 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 381 ChildProcessInfo::ProcessType type; 382 std::string type_str = cmd_line->GetSwitchValueASCII(switches::kProcessType); 383 if (type_str == switches::kRendererProcess) { 384 type = ChildProcessInfo::RENDER_PROCESS; 385 } else if (type_str == switches::kExtensionProcess) { 386 // Extensions are just renderers with another name. 387 type = ChildProcessInfo::RENDER_PROCESS; 388 } else if (type_str == switches::kPluginProcess) { 389 type = ChildProcessInfo::PLUGIN_PROCESS; 390 } else if (type_str == switches::kWorkerProcess) { 391 type = ChildProcessInfo::WORKER_PROCESS; 392 } else if (type_str == switches::kNaClLoaderProcess) { 393 type = ChildProcessInfo::NACL_LOADER_PROCESS; 394 } else if (type_str == switches::kUtilityProcess) { 395 type = ChildProcessInfo::UTILITY_PROCESS; 396 } else if (type_str == switches::kNaClBrokerProcess) { 397 type = ChildProcessInfo::NACL_BROKER_PROCESS; 398 } else if (type_str == switches::kGpuProcess) { 399 type = ChildProcessInfo::GPU_PROCESS; 400 } else { 401 NOTREACHED(); 402 return 0; 403 } 404 405 bool in_sandbox = 406 (type != ChildProcessInfo::NACL_BROKER_PROCESS) && 407 !browser_command_line.HasSwitch(switches::kNoSandbox) && 408 (type != ChildProcessInfo::PLUGIN_PROCESS || 409 browser_command_line.HasSwitch(switches::kSafePlugins)) && 410 (type != ChildProcessInfo::GPU_PROCESS); 411#if !defined (GOOGLE_CHROME_BUILD) 412 if (browser_command_line.HasSwitch(switches::kInProcessPlugins)) { 413 // In process plugins won't work if the sandbox is enabled. 414 in_sandbox = false; 415 } 416#endif 417 if (browser_command_line.HasSwitch(switches::kEnableExperimentalWebGL) && 418 browser_command_line.HasSwitch(switches::kInProcessWebGL)) { 419 // In process WebGL won't work if the sandbox is enabled. 420 in_sandbox = false; 421 } 422 423 // Propagate the Chrome Frame flag to sandboxed processes if present. 424 if (browser_command_line.HasSwitch(switches::kChromeFrame)) { 425 if (!cmd_line->HasSwitch(switches::kChromeFrame)) { 426 cmd_line->AppendSwitch(switches::kChromeFrame); 427 } 428 } 429 430 bool child_needs_help = 431 DebugFlags::ProcessDebugFlags(cmd_line, type, in_sandbox); 432 433 // Prefetch hints on windows: 434 // Using a different prefetch profile per process type will allow Windows 435 // to create separate pretetch settings for browser, renderer etc. 436 cmd_line->AppendLooseValue(StringPrintf(L"/prefetch:%d", type)); 437 438 if (!in_sandbox) { 439 base::LaunchApp(*cmd_line, false, false, &process); 440 return process; 441 } 442 443 sandbox::ResultCode result; 444 PROCESS_INFORMATION target = {0}; 445 sandbox::TargetPolicy* policy = g_broker_services->CreatePolicy(); 446 447 bool on_sandbox_desktop = false; 448 if (type == ChildProcessInfo::PLUGIN_PROCESS) { 449 if (!AddPolicyForPlugin(cmd_line, policy)) 450 return 0; 451 } else { 452 AddPolicyForRenderer(policy, &on_sandbox_desktop); 453 454 if (type_str != switches::kRendererProcess) { 455 // Hack for Google Desktop crash. Trick GD into not injecting its DLL into 456 // this subprocess. See 457 // http://code.google.com/p/chromium/issues/detail?id=25580 458 cmd_line->AppendSwitchWithValue("ignored", " --type=renderer "); 459 } 460 } 461 462 if (!exposed_dir.empty()) { 463 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, 464 sandbox::TargetPolicy::FILES_ALLOW_ANY, 465 exposed_dir.ToWStringHack().c_str()); 466 if (result != sandbox::SBOX_ALL_OK) 467 return 0; 468 469 FilePath exposed_files = exposed_dir.AppendASCII("*"); 470 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, 471 sandbox::TargetPolicy::FILES_ALLOW_ANY, 472 exposed_files.ToWStringHack().c_str()); 473 if (result != sandbox::SBOX_ALL_OK) 474 return 0; 475 } 476 477 if (!AddGenericPolicy(policy)) { 478 NOTREACHED(); 479 return 0; 480 } 481 482 result = g_broker_services->SpawnTarget( 483 cmd_line->program().c_str(), 484 cmd_line->command_line_string().c_str(), 485 policy, &target); 486 policy->Release(); 487 488 if (sandbox::SBOX_ALL_OK != result) 489 return 0; 490 491 ResumeThread(target.hThread); 492 CloseHandle(target.hThread); 493 process = target.hProcess; 494 495 // Help the process a little. It can't start the debugger by itself if 496 // the process is in a sandbox. 497 if (child_needs_help) 498 DebugUtil::SpawnDebuggerOnProcess(target.dwProcessId); 499 500 return process; 501} 502 503} // namespace sandbox 504