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.
5 #include "chrome/common/sandbox_policy.h"
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/string_util.h"
17 #include "base/trace_event.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"
26 static sandbox::BrokerServices
* g_broker_services
= NULL
;
30 // The DLLs listed here are known (or under strong suspicion) of causing crashes
31 // when they are loaded in the renderer.
32 const 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.
81 enum PluginPolicyCategory
{
83 PLUGIN_GROUP_UNTRUSTED
,
86 // Returns the policy category for the plugin dll.
87 PluginPolicyCategory
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
);
96 while (end_item
!= std::wstring::npos
) {
97 end_item
= list
.find(L
",", pos
);
99 size_t size_item
= (end_item
== std::wstring::npos
) ? end_item
:
101 std::wstring item
= list
.substr(pos
, size_item
);
102 if (!item
.empty() && item
== plugin_dll
)
103 return PLUGIN_GROUP_TRUSTED
;
108 return PLUGIN_GROUP_UNTRUSTED
;
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.
114 bool 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
))
122 file_util::AppendToPath(&directory
, sub_dir
);
123 file_util::AbsolutePath(&directory
);
126 sandbox::ResultCode result
;
127 result
= policy
->AddRule(sandbox::TargetPolicy::SUBSYS_FILES
, access
,
129 if (result
!= sandbox::SBOX_ALL_OK
)
133 file_util::AppendToPath(&directory
, L
"*");
135 // Add the version of the path that ends with a separator.
136 file_util::AppendToPath(&directory
, L
"");
138 result
= policy
->AddRule(sandbox::TargetPolicy::SUBSYS_FILES
, access
,
140 if (result
!= sandbox::SBOX_ALL_OK
)
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.
148 bool 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
,
154 if (result
!= sandbox::SBOX_ALL_OK
)
158 result
= policy
->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY
, access
,
160 if (result
!= sandbox::SBOX_ALL_OK
)
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.
169 void 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
]);
180 // Adds the generic policy rules to a sandbox TargetPolicy.
181 bool AddGenericPolicy(sandbox::TargetPolicy
* policy
) {
182 sandbox::ResultCode result
;
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
)
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
)
197 // Add the policy for debug message only in debug
199 std::wstring debug_message
;
200 if (!PathService::Get(chrome::DIR_APP
, &debug_message
))
202 if (!win_util::ConvertToLongPath(debug_message
, &debug_message
))
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
)
215 // Creates a sandbox without any restriction.
216 bool ApplyPolicyForTrustedPlugin(sandbox::TargetPolicy
* policy
) {
217 policy
->SetJobLevel(sandbox::JOB_UNPROTECTED
, 0);
218 policy
->SetTokenLevel(sandbox::USER_UNPROTECTED
, sandbox::USER_UNPROTECTED
);
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
225 bool ApplyPolicyForUntrustedPlugin(sandbox::TargetPolicy
* policy
) {
226 policy
->SetJobLevel(sandbox::JOB_UNPROTECTED
, 0);
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
232 initial_token
= sandbox::USER_RESTRICTED_SAME_ACCESS
;
234 policy
->SetTokenLevel(initial_token
, sandbox::USER_LIMITED
);
235 policy
->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW
);
237 if (!AddDirectory(base::DIR_TEMP
, NULL
, true,
238 sandbox::TargetPolicy::FILES_ALLOW_ANY
, policy
))
241 if (!AddDirectory(base::DIR_IE_INTERNET_CACHE
, NULL
, true,
242 sandbox::TargetPolicy::FILES_ALLOW_ANY
, policy
))
245 if (!AddDirectory(base::DIR_APP_DATA
, NULL
, true,
246 sandbox::TargetPolicy::FILES_ALLOW_READONLY
,
250 if (!AddDirectory(base::DIR_PROFILE
, NULL
, false, /*not recursive*/
251 sandbox::TargetPolicy::FILES_ALLOW_READONLY
,
255 if (!AddDirectory(base::DIR_APP_DATA
, L
"Adobe", true,
256 sandbox::TargetPolicy::FILES_ALLOW_ANY
,
260 if (!AddDirectory(base::DIR_APP_DATA
, L
"Macromedia", true,
261 sandbox::TargetPolicy::FILES_ALLOW_ANY
,
265 if (!AddDirectory(base::DIR_LOCAL_APP_DATA
, NULL
, true,
266 sandbox::TargetPolicy::FILES_ALLOW_READONLY
,
270 if (!AddKeyAndSubkeys(L
"HKEY_CURRENT_USER\\SOFTWARE\\ADOBE",
271 sandbox::TargetPolicy::REG_ALLOW_ANY
,
275 if (!AddKeyAndSubkeys(L
"HKEY_CURRENT_USER\\SOFTWARE\\MACROMEDIA",
276 sandbox::TargetPolicy::REG_ALLOW_ANY
,
280 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA
) {
281 if (!AddKeyAndSubkeys(L
"HKEY_CURRENT_USER\\SOFTWARE\\AppDataLow",
282 sandbox::TargetPolicy::REG_ALLOW_ANY
,
286 if (!AddDirectory(base::DIR_LOCAL_APP_DATA_LOW
, NULL
, true,
287 sandbox::TargetPolicy::FILES_ALLOW_ANY
,
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
294 if (!AddDirectory(base::DIR_APP_DATA
, L
"..", false,
295 sandbox::TargetPolicy::FILES_ALLOW_READONLY
,
303 // Creates a sandbox for the built-in flash plugin running in a restricted
304 // environment. This is a work in progress and for the time being do not
305 // pay attention to the duplication between this function and the above
306 // function. For more information see bug 50796.
307 bool ApplyPolicyForBuiltInFlashPlugin(sandbox::TargetPolicy
* policy
) {
308 // TODO(cpu): Lock down the job level more.
309 policy
->SetJobLevel(sandbox::JOB_UNPROTECTED
, 0);
311 sandbox::TokenLevel initial_token
= sandbox::USER_UNPROTECTED
;
312 if (win_util::GetWinVersion() > win_util::WINVERSION_XP
)
313 initial_token
= sandbox::USER_RESTRICTED_SAME_ACCESS
;
314 policy
->SetTokenLevel(initial_token
, sandbox::USER_LIMITED
);
316 policy
->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW
);
318 // TODO(cpu): Proxy registry access and remove these policies.
319 if (!AddKeyAndSubkeys(L
"HKEY_CURRENT_USER\\SOFTWARE\\ADOBE",
320 sandbox::TargetPolicy::REG_ALLOW_ANY
,
324 if (!AddKeyAndSubkeys(L
"HKEY_CURRENT_USER\\SOFTWARE\\MACROMEDIA",
325 sandbox::TargetPolicy::REG_ALLOW_ANY
,
329 // Use a different data folder for flash data. This needs to be
330 // reverted once we stop the experiments.
332 PathService::Get(chrome::DIR_USER_DATA
, &flash_path
);
333 flash_path
= flash_path
.AppendASCII("swflash");
334 ::SetEnvironmentVariableW(L
"CHROME_FLASH_ROOT",
335 flash_path
.ToWStringHack().c_str());
339 // Adds the custom policy rules for a given plugin. |trusted_plugins| contains
340 // the comma separate list of plugin dll names that should not be sandboxed.
341 bool AddPolicyForPlugin(const CommandLine
* cmd_line
,
342 sandbox::TargetPolicy
* policy
) {
343 std::wstring plugin_dll
= cmd_line
->
344 GetSwitchValueNative(switches::kPluginPath
);
345 std::wstring trusted_plugins
= CommandLine::ForCurrentProcess()->
346 GetSwitchValueNative(switches::kTrustedPlugins
);
347 // Add the policy for the pipes.
348 sandbox::ResultCode result
= sandbox::SBOX_ALL_OK
;
349 result
= policy
->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES
,
350 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY
,
351 L
"\\\\.\\pipe\\chrome.*");
352 if (result
!= sandbox::SBOX_ALL_OK
) {
357 // The built-in flash gets a custom, more restricted sandbox.
358 FilePath builtin_flash
;
359 if (PathService::Get(chrome::FILE_FLASH_PLUGIN
, &builtin_flash
)) {
360 FilePath
plugin_path(plugin_dll
);
361 if (plugin_path
== builtin_flash
)
362 return ApplyPolicyForBuiltInFlashPlugin(policy
);
365 PluginPolicyCategory policy_category
=
366 GetPolicyCategoryForPlugin(plugin_dll
, trusted_plugins
);
368 switch (policy_category
) {
369 case PLUGIN_GROUP_TRUSTED
:
370 return ApplyPolicyForTrustedPlugin(policy
);
371 case PLUGIN_GROUP_UNTRUSTED
:
372 return ApplyPolicyForUntrustedPlugin(policy
);
381 void AddPolicyForRenderer(sandbox::TargetPolicy
* policy
,
382 bool* on_sandbox_desktop
) {
383 policy
->SetJobLevel(sandbox::JOB_LOCKDOWN
, 0);
385 sandbox::TokenLevel initial_token
= sandbox::USER_UNPROTECTED
;
386 if (win_util::GetWinVersion() > win_util::WINVERSION_XP
) {
387 // On 2003/Vista the initial token has to be restricted if the main
388 // token is restricted.
389 initial_token
= sandbox::USER_RESTRICTED_SAME_ACCESS
;
392 policy
->SetTokenLevel(initial_token
, sandbox::USER_LOCKDOWN
);
393 policy
->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW
);
395 bool use_winsta
= !CommandLine::ForCurrentProcess()->HasSwitch(
396 switches::kDisableAltWinstation
);
398 if (sandbox::SBOX_ALL_OK
== policy
->SetAlternateDesktop(use_winsta
)) {
399 *on_sandbox_desktop
= true;
401 *on_sandbox_desktop
= false;
402 DLOG(WARNING
) << "Failed to apply desktop security to the renderer";
405 AddDllEvictionPolicy(policy
);
412 void InitBrokerServices(sandbox::BrokerServices
* broker_services
) {
413 // TODO(abarth): DCHECK(CalledOnValidThread());
414 // See <http://b/1287166>.
415 CHECK(broker_services
);
416 CHECK(!g_broker_services
);
417 broker_services
->Init();
418 g_broker_services
= broker_services
;
421 base::ProcessHandle
StartProcessWithAccess(CommandLine
* cmd_line
,
422 const FilePath
& exposed_dir
) {
423 base::ProcessHandle process
= 0;
424 const CommandLine
& browser_command_line
= *CommandLine::ForCurrentProcess();
425 ChildProcessInfo::ProcessType type
;
426 std::string type_str
= cmd_line
->GetSwitchValueASCII(switches::kProcessType
);
427 if (type_str
== switches::kRendererProcess
) {
428 type
= ChildProcessInfo::RENDER_PROCESS
;
429 } else if (type_str
== switches::kExtensionProcess
) {
430 // Extensions are just renderers with another name.
431 type
= ChildProcessInfo::RENDER_PROCESS
;
432 } else if (type_str
== switches::kPluginProcess
) {
433 type
= ChildProcessInfo::PLUGIN_PROCESS
;
434 } else if (type_str
== switches::kWorkerProcess
) {
435 type
= ChildProcessInfo::WORKER_PROCESS
;
436 } else if (type_str
== switches::kNaClLoaderProcess
) {
437 type
= ChildProcessInfo::NACL_LOADER_PROCESS
;
438 } else if (type_str
== switches::kUtilityProcess
) {
439 type
= ChildProcessInfo::UTILITY_PROCESS
;
440 } else if (type_str
== switches::kNaClBrokerProcess
) {
441 type
= ChildProcessInfo::NACL_BROKER_PROCESS
;
442 } else if (type_str
== switches::kGpuProcess
) {
443 type
= ChildProcessInfo::GPU_PROCESS
;
449 TRACE_EVENT_BEGIN("StartProcessWithAccess", 0, type_str
);
452 (type
!= ChildProcessInfo::NACL_BROKER_PROCESS
) &&
453 !browser_command_line
.HasSwitch(switches::kNoSandbox
) &&
454 (type
!= ChildProcessInfo::PLUGIN_PROCESS
||
455 browser_command_line
.HasSwitch(switches::kSafePlugins
)) &&
456 (type
!= ChildProcessInfo::GPU_PROCESS
);
457 #if !defined (GOOGLE_CHROME_BUILD)
458 if (browser_command_line
.HasSwitch(switches::kInProcessPlugins
)) {
459 // In process plugins won't work if the sandbox is enabled.
463 if (!browser_command_line
.HasSwitch(switches::kDisableExperimentalWebGL
) &&
464 browser_command_line
.HasSwitch(switches::kInProcessWebGL
)) {
465 // In process WebGL won't work if the sandbox is enabled.
469 // Propagate the Chrome Frame flag to sandboxed processes if present.
470 if (browser_command_line
.HasSwitch(switches::kChromeFrame
)) {
471 if (!cmd_line
->HasSwitch(switches::kChromeFrame
)) {
472 cmd_line
->AppendSwitch(switches::kChromeFrame
);
476 bool child_needs_help
=
477 DebugFlags::ProcessDebugFlags(cmd_line
, type
, in_sandbox
);
479 // Prefetch hints on windows:
480 // Using a different prefetch profile per process type will allow Windows
481 // to create separate pretetch settings for browser, renderer etc.
482 cmd_line
->AppendArg(StringPrintf("/prefetch:%d", type
));
485 base::LaunchApp(*cmd_line
, false, false, &process
);
489 sandbox::ResultCode result
;
490 PROCESS_INFORMATION target
= {0};
491 sandbox::TargetPolicy
* policy
= g_broker_services
->CreatePolicy();
493 bool on_sandbox_desktop
= false;
494 if (type
== ChildProcessInfo::PLUGIN_PROCESS
) {
495 if (!AddPolicyForPlugin(cmd_line
, policy
))
498 AddPolicyForRenderer(policy
, &on_sandbox_desktop
);
500 if (type_str
!= switches::kRendererProcess
) {
501 // Hack for Google Desktop crash. Trick GD into not injecting its DLL into
502 // this subprocess. See
503 // http://code.google.com/p/chromium/issues/detail?id=25580
504 cmd_line
->AppendSwitchASCII("ignored", " --type=renderer ");
508 if (!exposed_dir
.empty()) {
509 result
= policy
->AddRule(sandbox::TargetPolicy::SUBSYS_FILES
,
510 sandbox::TargetPolicy::FILES_ALLOW_ANY
,
511 exposed_dir
.ToWStringHack().c_str());
512 if (result
!= sandbox::SBOX_ALL_OK
)
515 FilePath exposed_files
= exposed_dir
.AppendASCII("*");
516 result
= policy
->AddRule(sandbox::TargetPolicy::SUBSYS_FILES
,
517 sandbox::TargetPolicy::FILES_ALLOW_ANY
,
518 exposed_files
.ToWStringHack().c_str());
519 if (result
!= sandbox::SBOX_ALL_OK
)
523 if (!AddGenericPolicy(policy
)) {
528 TRACE_EVENT_BEGIN("StartProcessWithAccess::LAUNCHPROCESS", 0, 0);
530 result
= g_broker_services
->SpawnTarget(
531 cmd_line
->program().c_str(),
532 cmd_line
->command_line_string().c_str(),
536 TRACE_EVENT_END("StartProcessWithAccess::LAUNCHPROCESS", 0, 0);
538 if (sandbox::SBOX_ALL_OK
!= result
)
541 ResumeThread(target
.hThread
);
542 CloseHandle(target
.hThread
);
543 process
= target
.hProcess
;
545 // Help the process a little. It can't start the debugger by itself if
546 // the process is in a sandbox.
547 if (child_needs_help
)
548 DebugUtil::SpawnDebuggerOnProcess(target
.dwProcessId
);
553 } // namespace sandbox