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