1 // Copyright (c) 2011 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 "base/command_line.h"
10 #include "base/debug/debugger.h"
11 #include "base/debug/trace_event.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/stringprintf.h"
17 #include "base/string_number_conversions.h"
18 #include "base/string_util.h"
19 #include "base/win/windows_version.h"
20 #include "chrome/common/chrome_constants.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "content/common/child_process_info.h"
24 #include "content/common/debug_flags.h"
25 #include "sandbox/src/sandbox.h"
26
27 static sandbox::BrokerServices* g_broker_services = NULL;
28
29 namespace {
30
31 // The DLLs listed here are known (or under strong suspicion) of causing crashes
32 // when they are loaded in the renderer. Note: at runtime we generate short
33 // versions of the dll name only if the dll has an extension.
34 const wchar_t* const kTroublesomeDlls[] = {
35 L"adialhk.dll", // Kaspersky Internet Security.
36 L"acpiz.dll", // Unknown.
37 L"avgrsstx.dll", // AVG 8.
38 L"babylonchromepi.dll", // Babylon translator.
39 L"btkeyind.dll", // Widcomm Bluetooth.
40 L"cmcsyshk.dll", // CMC Internet Security.
41 L"cooliris.dll", // CoolIris.
42 L"dockshellhook.dll", // Stardock Objectdock.
43 L"googledesktopnetwork3.dll", // Google Desktop Search v5.
44 L"fwhook.dll", // PC Tools Firewall Plus.
45 L"hookprocesscreation.dll", // Blumentals Program protector.
46 L"hookterminateapis.dll", // Blumentals and Cyberprinter.
47 L"hookprintapis.dll", // Cyberprinter.
48 L"imon.dll", // NOD32 Antivirus.
49 L"ioloHL.dll", // Iolo (System Mechanic).
50 L"kloehk.dll", // Kaspersky Internet Security.
51 L"lawenforcer.dll", // Spyware-Browser AntiSpyware (Spybro).
52 L"libdivx.dll", // DivX.
53 L"lvprcinj01.dll", // Logitech QuickCam.
54 L"madchook.dll", // Madshi (generic hooking library).
55 L"mdnsnsp.dll", // Bonjour.
56 L"moonsysh.dll", // Moon Secure Antivirus.
57 L"npdivx32.dll", // DivX.
58 L"npggNT.des", // GameGuard 2008.
59 L"npggNT.dll", // GameGuard (older).
60 L"oawatch.dll", // Online Armor.
61 L"pavhook.dll", // Panda Internet Security.
62 L"pavshook.dll", // Panda Antivirus.
63 L"pavshookwow.dll", // Panda Antivirus.
64 L"pctavhook.dll", // PC Tools Antivirus.
65 L"pctgmhk.dll", // PC Tools Spyware Doctor.
66 L"prntrack.dll", // Pharos Systems.
67 L"radhslib.dll", // Radiant Naomi Internet Filter.
68 L"radprlib.dll", // Radiant Naomi Internet Filter.
69 L"rapportnikko.dll", // Trustware Rapport.
70 L"rlhook.dll", // Trustware Bufferzone.
71 L"rooksdol.dll", // Trustware Rapport.
72 L"rpchromebrowserrecordhelper.dll", // RealPlayer.
73 L"rpmainbrowserrecordplugin.dll", // RealPlayer.
74 L"r3hook.dll", // Kaspersky Internet Security.
75 L"sahook.dll", // McAfee Site Advisor.
76 L"sbrige.dll", // Unknown.
77 L"sc2hook.dll", // Supercopier 2.
78 L"sguard.dll", // Iolo (System Guard).
79 L"smum32.dll", // Spyware Doctor version 6.
80 L"smumhook.dll", // Spyware Doctor version 5.
81 L"ssldivx.dll", // DivX.
82 L"syncor11.dll", // SynthCore Midi interface.
83 L"systools.dll", // Panda Antivirus.
84 L"tfwah.dll", // Threatfire (PC tools).
85 L"ycwebcamerasource.ax", // Cyberlink Camera helper.
86 L"wblind.dll", // Stardock Object desktop.
87 L"wbhelp.dll", // Stardock Object desktop.
88 L"winstylerthemehelper.dll" // Tuneup utilities 2006.
89 };
90
91 enum PluginPolicyCategory {
92 PLUGIN_GROUP_TRUSTED,
93 PLUGIN_GROUP_UNTRUSTED,
94 };
95
96 // Returns the policy category for the plugin dll.
GetPolicyCategoryForPlugin(const std::wstring & dll,const std::wstring & list)97 PluginPolicyCategory GetPolicyCategoryForPlugin(
98 const std::wstring& dll,
99 const std::wstring& list) {
100 std::wstring filename = FilePath(dll).BaseName().value();
101 std::wstring plugin_dll = StringToLowerASCII(filename);
102 std::wstring trusted_plugins = StringToLowerASCII(list);
103
104 size_t pos = 0;
105 size_t end_item = 0;
106 while (end_item != std::wstring::npos) {
107 end_item = list.find(L",", pos);
108
109 size_t size_item = (end_item == std::wstring::npos) ? end_item :
110 end_item - pos;
111 std::wstring item = list.substr(pos, size_item);
112 if (!item.empty() && item == plugin_dll)
113 return PLUGIN_GROUP_TRUSTED;
114
115 pos = end_item + 1;
116 }
117
118 return PLUGIN_GROUP_UNTRUSTED;
119 }
120
121 // Adds the policy rules for the path and path\ with the semantic |access|.
122 // If |children| is set to true, we need to add the wildcard rules to also
123 // apply the rule to the subfiles and subfolders.
AddDirectory(int path,const wchar_t * sub_dir,bool children,sandbox::TargetPolicy::Semantics access,sandbox::TargetPolicy * policy)124 bool AddDirectory(int path, const wchar_t* sub_dir, bool children,
125 sandbox::TargetPolicy::Semantics access,
126 sandbox::TargetPolicy* policy) {
127 FilePath directory;
128 if (!PathService::Get(path, &directory))
129 return false;
130
131 if (sub_dir) {
132 directory = directory.Append(sub_dir);
133 file_util::AbsolutePath(&directory);
134 }
135
136 sandbox::ResultCode result;
137 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access,
138 directory.value().c_str());
139 if (result != sandbox::SBOX_ALL_OK)
140 return false;
141
142 std::wstring directory_str = directory.value() + L"\\";
143 if (children)
144 directory_str += L"*";
145 // Otherwise, add the version of the path that ends with a separator.
146
147 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access,
148 directory_str.c_str());
149 if (result != sandbox::SBOX_ALL_OK)
150 return false;
151
152 return true;
153 }
154
155 // Adds the policy rules for the path and path\* with the semantic |access|.
156 // We need to add the wildcard rules to also apply the rule to the subkeys.
AddKeyAndSubkeys(std::wstring key,sandbox::TargetPolicy::Semantics access,sandbox::TargetPolicy * policy)157 bool AddKeyAndSubkeys(std::wstring key,
158 sandbox::TargetPolicy::Semantics access,
159 sandbox::TargetPolicy* policy) {
160 sandbox::ResultCode result;
161 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access,
162 key.c_str());
163 if (result != sandbox::SBOX_ALL_OK)
164 return false;
165
166 key += L"\\*";
167 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access,
168 key.c_str());
169 if (result != sandbox::SBOX_ALL_OK)
170 return false;
171
172 return true;
173 }
174
175 // Compares the loaded |module| file name matches |module_name|.
IsExpandedModuleName(HMODULE module,const wchar_t * module_name)176 bool IsExpandedModuleName(HMODULE module, const wchar_t* module_name) {
177 wchar_t path[MAX_PATH];
178 DWORD sz = ::GetModuleFileNameW(module, path, arraysize(path));
179 if ((sz == arraysize(path)) || (sz == 0)) {
180 // XP does not set the last error properly, so we bail out anyway.
181 return false;
182 }
183 if (!::GetLongPathName(path, path, arraysize(path)))
184 return false;
185 FilePath fname(path);
186 return (fname.BaseName().value() == module_name);
187 }
188
189 // Adds a single dll by |module_name| into the |policy| blacklist.
190 // To minimize the list we only add an unload policy only if the dll is
191 // also loaded in this process. All the injected dlls of interest do this.
BlacklistAddOneDll(const wchar_t * module_name,sandbox::TargetPolicy * policy)192 void BlacklistAddOneDll(const wchar_t* module_name,
193 sandbox::TargetPolicy* policy) {
194 HMODULE module = ::GetModuleHandleW(module_name);
195 if (!module) {
196 // The module could have been loaded with a 8.3 short name. We use
197 // the most common case: 'thelongname.dll' becomes 'thelon~1.dll'.
198 std::wstring name(module_name);
199 size_t period = name.rfind(L'.');
200 DCHECK_NE(std::string::npos, period);
201 DCHECK_LE(3U, (name.size() - period));
202 if (period <= 8)
203 return;
204 std::wstring alt_name = name.substr(0, 6) + L"~1";
205 alt_name += name.substr(period, name.size());
206 module = ::GetModuleHandleW(alt_name.c_str());
207 if (!module)
208 return;
209 // We found it, but because it only has 6 significant letters, we
210 // want to make sure it is the right one.
211 if (!IsExpandedModuleName(module, module_name))
212 return;
213 // Found a match. We add both forms to the policy.
214 policy->AddDllToUnload(alt_name.c_str());
215 }
216 policy->AddDllToUnload(module_name);
217 VLOG(1) << "dll to unload found: " << module_name;
218 return;
219 }
220
221 // Adds policy rules for unloaded the known dlls that cause chrome to crash.
222 // Eviction of injected DLLs is done by the sandbox so that the injected module
223 // does not get a chance to execute any code.
AddDllEvictionPolicy(sandbox::TargetPolicy * policy)224 void AddDllEvictionPolicy(sandbox::TargetPolicy* policy) {
225 for (int ix = 0; ix != arraysize(kTroublesomeDlls); ++ix)
226 BlacklistAddOneDll(kTroublesomeDlls[ix], policy);
227 }
228
229 // Adds the generic policy rules to a sandbox TargetPolicy.
AddGenericPolicy(sandbox::TargetPolicy * policy)230 bool AddGenericPolicy(sandbox::TargetPolicy* policy) {
231 sandbox::ResultCode result;
232
233 // Add the policy for the pipes
234 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
235 sandbox::TargetPolicy::FILES_ALLOW_ANY,
236 L"\\??\\pipe\\chrome.*");
237 if (result != sandbox::SBOX_ALL_OK)
238 return false;
239
240 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
241 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
242 L"\\\\.\\pipe\\chrome.nacl.*");
243 if (result != sandbox::SBOX_ALL_OK)
244 return false;
245
246 // Add the policy for debug message only in debug
247 #ifndef NDEBUG
248 FilePath app_dir;
249 if (!PathService::Get(chrome::DIR_APP, &app_dir))
250 return false;
251
252 wchar_t long_path_buf[MAX_PATH];
253 DWORD long_path_return_value = GetLongPathName(app_dir.value().c_str(),
254 long_path_buf,
255 MAX_PATH);
256 if (long_path_return_value == 0 || long_path_return_value >= MAX_PATH)
257 return false;
258
259 string16 debug_message(long_path_buf);
260 file_util::AppendToPath(&debug_message, L"debug_message.exe");
261 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_PROCESS,
262 sandbox::TargetPolicy::PROCESS_MIN_EXEC,
263 debug_message.c_str());
264 if (result != sandbox::SBOX_ALL_OK)
265 return false;
266 #endif // NDEBUG
267
268 return true;
269 }
270
271 // Creates a sandbox without any restriction.
ApplyPolicyForTrustedPlugin(sandbox::TargetPolicy * policy)272 bool ApplyPolicyForTrustedPlugin(sandbox::TargetPolicy* policy) {
273 policy->SetJobLevel(sandbox::JOB_UNPROTECTED, 0);
274 policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED);
275 return true;
276 }
277
278 // Creates a sandbox with the plugin running in a restricted environment.
279 // Only the "Users" and "Everyone" groups are enabled in the token. The User SID
280 // is disabled.
ApplyPolicyForUntrustedPlugin(sandbox::TargetPolicy * policy)281 bool ApplyPolicyForUntrustedPlugin(sandbox::TargetPolicy* policy) {
282 policy->SetJobLevel(sandbox::JOB_UNPROTECTED, 0);
283
284 sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED;
285 if (base::win::GetVersion() > base::win::VERSION_XP) {
286 // On 2003/Vista the initial token has to be restricted if the main token
287 // is restricted.
288 initial_token = sandbox::USER_RESTRICTED_SAME_ACCESS;
289 }
290 policy->SetTokenLevel(initial_token, sandbox::USER_LIMITED);
291 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
292
293 if (!AddDirectory(base::DIR_TEMP, NULL, true,
294 sandbox::TargetPolicy::FILES_ALLOW_ANY, policy))
295 return false;
296
297 if (!AddDirectory(base::DIR_IE_INTERNET_CACHE, NULL, true,
298 sandbox::TargetPolicy::FILES_ALLOW_ANY, policy))
299 return false;
300
301 if (!AddDirectory(base::DIR_APP_DATA, NULL, true,
302 sandbox::TargetPolicy::FILES_ALLOW_READONLY,
303 policy))
304 return false;
305
306 if (!AddDirectory(base::DIR_PROFILE, NULL, false, /*not recursive*/
307 sandbox::TargetPolicy::FILES_ALLOW_READONLY,
308 policy))
309 return false;
310
311 if (!AddDirectory(base::DIR_APP_DATA, L"Adobe", true,
312 sandbox::TargetPolicy::FILES_ALLOW_ANY,
313 policy))
314 return false;
315
316 if (!AddDirectory(base::DIR_APP_DATA, L"Macromedia", true,
317 sandbox::TargetPolicy::FILES_ALLOW_ANY,
318 policy))
319 return false;
320
321 if (!AddDirectory(base::DIR_LOCAL_APP_DATA, NULL, true,
322 sandbox::TargetPolicy::FILES_ALLOW_READONLY,
323 policy))
324 return false;
325
326 if (!AddKeyAndSubkeys(L"HKEY_CURRENT_USER\\SOFTWARE\\ADOBE",
327 sandbox::TargetPolicy::REG_ALLOW_ANY,
328 policy))
329 return false;
330
331 if (!AddKeyAndSubkeys(L"HKEY_CURRENT_USER\\SOFTWARE\\MACROMEDIA",
332 sandbox::TargetPolicy::REG_ALLOW_ANY,
333 policy))
334 return false;
335
336 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
337 if (!AddKeyAndSubkeys(L"HKEY_CURRENT_USER\\SOFTWARE\\AppDataLow",
338 sandbox::TargetPolicy::REG_ALLOW_ANY,
339 policy))
340 return false;
341
342 if (!AddDirectory(base::DIR_LOCAL_APP_DATA_LOW, NULL, true,
343 sandbox::TargetPolicy::FILES_ALLOW_ANY,
344 policy))
345 return false;
346
347 // DIR_APP_DATA is AppData\Roaming, but Adobe needs to do a directory
348 // listing in AppData directly, so we add a non-recursive policy for
349 // AppData itself.
350 if (!AddDirectory(base::DIR_APP_DATA, L"..", false,
351 sandbox::TargetPolicy::FILES_ALLOW_READONLY,
352 policy))
353 return false;
354 }
355
356 return true;
357 }
358
359 // Launches the privileged flash broker, used when flash is sandboxed.
360 // The broker is the same flash dll, except that it uses a different
361 // entrypoint (BrokerMain) and it is hosted in windows' generic surrogate
362 // process rundll32. After launching the broker we need to pass to
363 // the flash plugin the process id of the broker via the command line
364 // using --flash-broker=pid.
365 // More info about rundll32 at http://support.microsoft.com/kb/164787.
LoadFlashBroker(const FilePath & plugin_path,CommandLine * cmd_line)366 bool LoadFlashBroker(const FilePath& plugin_path, CommandLine* cmd_line) {
367 FilePath rundll;
368 if (!PathService::Get(base::DIR_SYSTEM, &rundll))
369 return false;
370 rundll = rundll.AppendASCII("rundll32.exe");
371 // Rundll32 cannot handle paths with spaces, so we use the short path.
372 wchar_t short_path[MAX_PATH];
373 if (0 == ::GetShortPathNameW(plugin_path.value().c_str(),
374 short_path, arraysize(short_path)))
375 return false;
376 // Here is the kicker, if the user has disabled 8.3 (short path) support
377 // on the volume GetShortPathNameW does not fail but simply returns the
378 // input path. In this case if the path had any spaces then rundll32 will
379 // incorrectly interpret its parameters. So we quote the path, even though
380 // the kb/164787 says you should not.
381 std::wstring cmd_final =
382 base::StringPrintf(L"%ls \"%ls\",BrokerMain browser=chrome",
383 rundll.value().c_str(),
384 short_path);
385 base::ProcessHandle process;
386 if (!base::LaunchApp(cmd_final, false, true, &process))
387 return false;
388
389 cmd_line->AppendSwitchASCII("flash-broker",
390 base::Int64ToString(::GetProcessId(process)));
391
392 // The flash broker, unders some circumstances can linger beyond the lifetime
393 // of the flash player, so we put it in a job object, when the browser
394 // terminates the job object is destroyed (by the OS) and the flash broker
395 // is terminated.
396 HANDLE job = ::CreateJobObjectW(NULL, NULL);
397 JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limits = {0};
398 job_limits.BasicLimitInformation.LimitFlags =
399 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
400 if (::SetInformationJobObject(job, JobObjectExtendedLimitInformation,
401 &job_limits, sizeof(job_limits))) {
402 ::AssignProcessToJobObject(job, process);
403 // Yes, we are leaking the object here. Read comment above.
404 } else {
405 ::CloseHandle(job);
406 return false;
407 }
408
409 ::CloseHandle(process);
410 return true;
411 }
412
413 // Creates a sandbox for the built-in flash plugin running in a restricted
414 // environment. This policy is in continual flux as flash changes
415 // capabilities. For more information see bug 50796.
ApplyPolicyForBuiltInFlashPlugin(sandbox::TargetPolicy * policy)416 bool ApplyPolicyForBuiltInFlashPlugin(sandbox::TargetPolicy* policy) {
417 policy->SetJobLevel(sandbox::JOB_UNPROTECTED, 0);
418 // Vista and Win7 get a weaker token but have low integrity.
419 if (base::win::GetVersion() > base::win::VERSION_XP) {
420 policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
421 sandbox::USER_INTERACTIVE);
422 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
423 } else {
424 policy->SetTokenLevel(sandbox::USER_UNPROTECTED,
425 sandbox::USER_LIMITED);
426
427 if (!AddKeyAndSubkeys(L"HKEY_LOCAL_MACHINE\\SOFTWARE",
428 sandbox::TargetPolicy::REG_ALLOW_READONLY,
429 policy))
430 return false;
431 if (!AddKeyAndSubkeys(L"HKEY_LOCAL_MACHINE\\SYSTEM",
432 sandbox::TargetPolicy::REG_ALLOW_READONLY,
433 policy))
434 return false;
435
436 if (!AddKeyAndSubkeys(L"HKEY_CURRENT_USER\\SOFTWARE",
437 sandbox::TargetPolicy::REG_ALLOW_READONLY,
438 policy))
439 return false;
440 }
441
442 AddDllEvictionPolicy(policy);
443 return true;
444 }
445
446 // Returns true of the plugin specified in |cmd_line| is the built-in
447 // flash plugin and optionally returns its full path in |flash_path|
IsBuiltInFlash(const CommandLine * cmd_line,FilePath * flash_path)448 bool IsBuiltInFlash(const CommandLine* cmd_line, FilePath* flash_path) {
449 std::wstring plugin_dll = cmd_line->
450 GetSwitchValueNative(switches::kPluginPath);
451
452 FilePath builtin_flash;
453 if (!PathService::Get(chrome::FILE_FLASH_PLUGIN, &builtin_flash))
454 return false;
455
456 FilePath plugin_path(plugin_dll);
457 if (plugin_path != builtin_flash)
458 return false;
459
460 if (flash_path)
461 *flash_path = plugin_path;
462 return true;
463 }
464
465
466 // Adds the custom policy rules for a given plugin. |trusted_plugins| contains
467 // the comma separate list of plugin dll names that should not be sandboxed.
AddPolicyForPlugin(CommandLine * cmd_line,sandbox::TargetPolicy * policy)468 bool AddPolicyForPlugin(CommandLine* cmd_line,
469 sandbox::TargetPolicy* policy) {
470 std::wstring plugin_dll = cmd_line->
471 GetSwitchValueNative(switches::kPluginPath);
472 std::wstring trusted_plugins = CommandLine::ForCurrentProcess()->
473 GetSwitchValueNative(switches::kTrustedPlugins);
474 // Add the policy for the pipes.
475 sandbox::ResultCode result = sandbox::SBOX_ALL_OK;
476 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
477 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
478 L"\\\\.\\pipe\\chrome.*");
479 if (result != sandbox::SBOX_ALL_OK) {
480 NOTREACHED();
481 return false;
482 }
483
484 // The built-in flash gets a custom, more restricted sandbox.
485 FilePath flash_path;
486 if (IsBuiltInFlash(cmd_line, &flash_path)) {
487 // Spawn the flash broker and apply sandbox policy.
488 if (!LoadFlashBroker(flash_path, cmd_line)) {
489 // Could not start the broker, use a very weak policy instead.
490 DLOG(WARNING) << "Failed to start flash broker";
491 return ApplyPolicyForTrustedPlugin(policy);
492 }
493 return ApplyPolicyForBuiltInFlashPlugin(policy);
494 }
495
496 PluginPolicyCategory policy_category =
497 GetPolicyCategoryForPlugin(plugin_dll, trusted_plugins);
498
499 switch (policy_category) {
500 case PLUGIN_GROUP_TRUSTED:
501 return ApplyPolicyForTrustedPlugin(policy);
502 case PLUGIN_GROUP_UNTRUSTED:
503 return ApplyPolicyForUntrustedPlugin(policy);
504 default:
505 NOTREACHED();
506 break;
507 }
508
509 return false;
510 }
511
512 // For the GPU process we gotten as far as USER_LIMITED. The next level
513 // which is USER_RESTRICTED breaks both the DirectX backend and the OpenGL
514 // backend. Note that the GPU process is connected to the interactive
515 // desktop.
516 // TODO(cpu): Lock down the sandbox more if possible.
517 // TODO(apatrick): Use D3D9Ex to render windowless.
AddPolicyForGPU(CommandLine *,sandbox::TargetPolicy * policy)518 bool AddPolicyForGPU(CommandLine*, sandbox::TargetPolicy* policy) {
519 policy->SetJobLevel(sandbox::JOB_UNPROTECTED, 0);
520
521 if (base::win::GetVersion() > base::win::VERSION_XP) {
522 policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
523 sandbox::USER_LIMITED);
524 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
525 } else {
526 policy->SetTokenLevel(sandbox::USER_UNPROTECTED,
527 sandbox::USER_LIMITED);
528 }
529
530 AddDllEvictionPolicy(policy);
531 return true;
532 }
533
AddPolicyForRenderer(sandbox::TargetPolicy * policy,bool * on_sandbox_desktop)534 void AddPolicyForRenderer(sandbox::TargetPolicy* policy,
535 bool* on_sandbox_desktop) {
536 policy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0);
537
538 sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED;
539 if (base::win::GetVersion() > base::win::VERSION_XP) {
540 // On 2003/Vista the initial token has to be restricted if the main
541 // token is restricted.
542 initial_token = sandbox::USER_RESTRICTED_SAME_ACCESS;
543 }
544
545 policy->SetTokenLevel(initial_token, sandbox::USER_LOCKDOWN);
546 policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
547
548 bool use_winsta = !CommandLine::ForCurrentProcess()->HasSwitch(
549 switches::kDisableAltWinstation);
550
551 if (sandbox::SBOX_ALL_OK == policy->SetAlternateDesktop(use_winsta)) {
552 *on_sandbox_desktop = true;
553 } else {
554 *on_sandbox_desktop = false;
555 DLOG(WARNING) << "Failed to apply desktop security to the renderer";
556 }
557
558 AddDllEvictionPolicy(policy);
559 }
560
561 } // namespace
562
563 namespace sandbox {
564
InitBrokerServices(sandbox::BrokerServices * broker_services)565 void InitBrokerServices(sandbox::BrokerServices* broker_services) {
566 // TODO(abarth): DCHECK(CalledOnValidThread());
567 // See <http://b/1287166>.
568 CHECK(broker_services);
569 CHECK(!g_broker_services);
570 broker_services->Init();
571 g_broker_services = broker_services;
572 }
573
StartProcessWithAccess(CommandLine * cmd_line,const FilePath & exposed_dir)574 base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line,
575 const FilePath& exposed_dir) {
576 base::ProcessHandle process = 0;
577 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
578 ChildProcessInfo::ProcessType type;
579 std::string type_str = cmd_line->GetSwitchValueASCII(switches::kProcessType);
580 if (type_str == switches::kRendererProcess) {
581 type = ChildProcessInfo::RENDER_PROCESS;
582 } else if (type_str == switches::kExtensionProcess) {
583 // Extensions are just renderers with another name.
584 type = ChildProcessInfo::RENDER_PROCESS;
585 } else if (type_str == switches::kPluginProcess) {
586 type = ChildProcessInfo::PLUGIN_PROCESS;
587 } else if (type_str == switches::kWorkerProcess) {
588 type = ChildProcessInfo::WORKER_PROCESS;
589 } else if (type_str == switches::kNaClLoaderProcess) {
590 type = ChildProcessInfo::NACL_LOADER_PROCESS;
591 } else if (type_str == switches::kUtilityProcess) {
592 type = ChildProcessInfo::UTILITY_PROCESS;
593 } else if (type_str == switches::kNaClBrokerProcess) {
594 type = ChildProcessInfo::NACL_BROKER_PROCESS;
595 } else if (type_str == switches::kGpuProcess) {
596 type = ChildProcessInfo::GPU_PROCESS;
597 } else if (type_str == switches::kPpapiPluginProcess) {
598 type = ChildProcessInfo::PPAPI_PLUGIN_PROCESS;
599 } else {
600 NOTREACHED();
601 return 0;
602 }
603
604 TRACE_EVENT_BEGIN("StartProcessWithAccess", 0, type_str);
605
606 // To decide if the process is going to be sandboxed we have two cases.
607 // First case: all process types except the nacl broker, and the plugin
608 // process are sandboxed by default.
609 bool in_sandbox =
610 (type != ChildProcessInfo::NACL_BROKER_PROCESS) &&
611 (type != ChildProcessInfo::PLUGIN_PROCESS);
612
613 // Second case: If it is the plugin process then it depends on it being
614 // the built-in flash, the user forcing plugins into sandbox or the
615 // the user explicitly excluding flash from the sandbox.
616 if (!in_sandbox && (type == ChildProcessInfo::PLUGIN_PROCESS)) {
617 in_sandbox = browser_command_line.HasSwitch(switches::kSafePlugins) ||
618 (IsBuiltInFlash(cmd_line, NULL) &&
619 (base::win::GetVersion() > base::win::VERSION_XP) &&
620 !browser_command_line.HasSwitch(switches::kDisableFlashSandbox));
621 }
622
623 // Third case: If it is the GPU process then it can be disabled by a
624 // command line flag.
625 if ((type == ChildProcessInfo::GPU_PROCESS) &&
626 (browser_command_line.HasSwitch(switches::kDisableGpuSandbox))) {
627 in_sandbox = false;
628 VLOG(1) << "GPU sandbox is disabled";
629 }
630
631 if (browser_command_line.HasSwitch(switches::kNoSandbox)) {
632 // The user has explicity opted-out from all sandboxing.
633 in_sandbox = false;
634 }
635
636 #if !defined (GOOGLE_CHROME_BUILD)
637 if (browser_command_line.HasSwitch(switches::kInProcessPlugins)) {
638 // In process plugins won't work if the sandbox is enabled.
639 in_sandbox = false;
640 }
641 #endif
642 if (!browser_command_line.HasSwitch(switches::kDisable3DAPIs) &&
643 !browser_command_line.HasSwitch(switches::kDisableExperimentalWebGL) &&
644 browser_command_line.HasSwitch(switches::kInProcessWebGL)) {
645 // In process WebGL won't work if the sandbox is enabled.
646 in_sandbox = false;
647 }
648
649 // Propagate the Chrome Frame flag to sandboxed processes if present.
650 if (browser_command_line.HasSwitch(switches::kChromeFrame)) {
651 if (!cmd_line->HasSwitch(switches::kChromeFrame)) {
652 cmd_line->AppendSwitch(switches::kChromeFrame);
653 }
654 }
655
656 bool child_needs_help =
657 DebugFlags::ProcessDebugFlags(cmd_line, type, in_sandbox);
658
659 // Prefetch hints on windows:
660 // Using a different prefetch profile per process type will allow Windows
661 // to create separate pretetch settings for browser, renderer etc.
662 cmd_line->AppendArg(base::StringPrintf("/prefetch:%d", type));
663
664 if (!in_sandbox) {
665 base::LaunchApp(*cmd_line, false, false, &process);
666 return process;
667 }
668
669 sandbox::ResultCode result;
670 PROCESS_INFORMATION target = {0};
671 sandbox::TargetPolicy* policy = g_broker_services->CreatePolicy();
672
673 bool on_sandbox_desktop = false;
674 if (type == ChildProcessInfo::PLUGIN_PROCESS) {
675 if (!AddPolicyForPlugin(cmd_line, policy))
676 return 0;
677 } else if (type == ChildProcessInfo::GPU_PROCESS) {
678 if (!AddPolicyForGPU(cmd_line, policy))
679 return 0;
680 } else {
681 AddPolicyForRenderer(policy, &on_sandbox_desktop);
682
683 if (type_str != switches::kRendererProcess) {
684 // Hack for Google Desktop crash. Trick GD into not injecting its DLL into
685 // this subprocess. See
686 // http://code.google.com/p/chromium/issues/detail?id=25580
687 cmd_line->AppendSwitchASCII("ignored", " --type=renderer ");
688 }
689 }
690
691 if (!exposed_dir.empty()) {
692 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
693 sandbox::TargetPolicy::FILES_ALLOW_ANY,
694 exposed_dir.value().c_str());
695 if (result != sandbox::SBOX_ALL_OK)
696 return 0;
697
698 FilePath exposed_files = exposed_dir.AppendASCII("*");
699 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
700 sandbox::TargetPolicy::FILES_ALLOW_ANY,
701 exposed_files.value().c_str());
702 if (result != sandbox::SBOX_ALL_OK)
703 return 0;
704 }
705
706 if (!AddGenericPolicy(policy)) {
707 NOTREACHED();
708 return 0;
709 }
710
711 TRACE_EVENT_BEGIN("StartProcessWithAccess::LAUNCHPROCESS", 0, 0);
712
713 result = g_broker_services->SpawnTarget(
714 cmd_line->GetProgram().value().c_str(),
715 cmd_line->command_line_string().c_str(),
716 policy, &target);
717 policy->Release();
718
719 TRACE_EVENT_END("StartProcessWithAccess::LAUNCHPROCESS", 0, 0);
720
721 if (sandbox::SBOX_ALL_OK != result)
722 return 0;
723
724 ResumeThread(target.hThread);
725 CloseHandle(target.hThread);
726 process = target.hProcess;
727
728 // Help the process a little. It can't start the debugger by itself if
729 // the process is in a sandbox.
730 if (child_needs_help)
731 base::debug::SpawnDebuggerOnProcess(target.dwProcessId);
732
733 return process;
734 }
735
736 } // namespace sandbox
737