Home | History | Annotate | Download | only in common
      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.
     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.
    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.
    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|.
    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.
    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.
    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.
    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.
    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.
    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.
    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.
    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|
    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.
    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.
    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 
    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 
    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 
    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