Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2012 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 "content/common/sandbox_win.h"
      6 
      7 #include <string>
      8 
      9 #include "base/base_switches.h"
     10 #include "base/command_line.h"
     11 #include "base/debug/debugger.h"
     12 #include "base/debug/profiler.h"
     13 #include "base/debug/trace_event.h"
     14 #include "base/file_util.h"
     15 #include "base/hash.h"
     16 #include "base/path_service.h"
     17 #include "base/process/launch.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/strings/stringprintf.h"
     20 #include "base/win/iat_patch_function.h"
     21 #include "base/win/scoped_handle.h"
     22 #include "base/win/scoped_process_information.h"
     23 #include "base/win/windows_version.h"
     24 #include "content/public/common/content_client.h"
     25 #include "content/public/common/content_switches.h"
     26 #include "content/public/common/sandbox_init.h"
     27 #include "content/public/common/sandboxed_process_launcher_delegate.h"
     28 #include "ipc/ipc_switches.h"
     29 #include "sandbox/win/src/process_mitigations.h"
     30 #include "sandbox/win/src/sandbox.h"
     31 #include "sandbox/win/src/sandbox_nt_util.h"
     32 #include "sandbox/win/src/win_utils.h"
     33 
     34 static sandbox::BrokerServices* g_broker_services = NULL;
     35 static sandbox::TargetServices* g_target_services = NULL;
     36 
     37 namespace content {
     38 namespace {
     39 
     40 // The DLLs listed here are known (or under strong suspicion) of causing crashes
     41 // when they are loaded in the renderer. Note: at runtime we generate short
     42 // versions of the dll name only if the dll has an extension.
     43 const wchar_t* const kTroublesomeDlls[] = {
     44   L"adialhk.dll",                 // Kaspersky Internet Security.
     45   L"acpiz.dll",                   // Unknown.
     46   L"avgrsstx.dll",                // AVG 8.
     47   L"babylonchromepi.dll",         // Babylon translator.
     48   L"btkeyind.dll",                // Widcomm Bluetooth.
     49   L"cmcsyshk.dll",                // CMC Internet Security.
     50   L"cmsetac.dll",                 // Unknown (suspected malware).
     51   L"cooliris.dll",                // CoolIris.
     52   L"dockshellhook.dll",           // Stardock Objectdock.
     53   L"easyhook32.dll",              // GDIPP and others.
     54   L"googledesktopnetwork3.dll",   // Google Desktop Search v5.
     55   L"fwhook.dll",                  // PC Tools Firewall Plus.
     56   L"hookprocesscreation.dll",     // Blumentals Program protector.
     57   L"hookterminateapis.dll",       // Blumentals and Cyberprinter.
     58   L"hookprintapis.dll",           // Cyberprinter.
     59   L"imon.dll",                    // NOD32 Antivirus.
     60   L"ioloHL.dll",                  // Iolo (System Mechanic).
     61   L"kloehk.dll",                  // Kaspersky Internet Security.
     62   L"lawenforcer.dll",             // Spyware-Browser AntiSpyware (Spybro).
     63   L"libdivx.dll",                 // DivX.
     64   L"lvprcinj01.dll",              // Logitech QuickCam.
     65   L"madchook.dll",                // Madshi (generic hooking library).
     66   L"mdnsnsp.dll",                 // Bonjour.
     67   L"moonsysh.dll",                // Moon Secure Antivirus.
     68   L"mpk.dll",                     // KGB Spy.
     69   L"npdivx32.dll",                // DivX.
     70   L"npggNT.des",                  // GameGuard 2008.
     71   L"npggNT.dll",                  // GameGuard (older).
     72   L"oawatch.dll",                 // Online Armor.
     73   L"pavhook.dll",                 // Panda Internet Security.
     74   L"pavlsphook.dll",              // Panda Antivirus.
     75   L"pavshook.dll",                // Panda Antivirus.
     76   L"pavshookwow.dll",             // Panda Antivirus.
     77   L"pctavhook.dll",               // PC Tools Antivirus.
     78   L"pctgmhk.dll",                 // PC Tools Spyware Doctor.
     79   L"prntrack.dll",                // Pharos Systems.
     80   L"protector.dll",               // Unknown (suspected malware).
     81   L"radhslib.dll",                // Radiant Naomi Internet Filter.
     82   L"radprlib.dll",                // Radiant Naomi Internet Filter.
     83   L"rapportnikko.dll",            // Trustware Rapport.
     84   L"rlhook.dll",                  // Trustware Bufferzone.
     85   L"rooksdol.dll",                // Trustware Rapport.
     86   L"rndlpepperbrowserrecordhelper.dll", // RealPlayer.
     87   L"rpchromebrowserrecordhelper.dll",   // RealPlayer.
     88   L"r3hook.dll",                  // Kaspersky Internet Security.
     89   L"sahook.dll",                  // McAfee Site Advisor.
     90   L"sbrige.dll",                  // Unknown.
     91   L"sc2hook.dll",                 // Supercopier 2.
     92   L"sdhook32.dll",                // Spybot - Search & Destroy Live Protection.
     93   L"sguard.dll",                  // Iolo (System Guard).
     94   L"smum32.dll",                  // Spyware Doctor version 6.
     95   L"smumhook.dll",                // Spyware Doctor version 5.
     96   L"ssldivx.dll",                 // DivX.
     97   L"syncor11.dll",                // SynthCore Midi interface.
     98   L"systools.dll",                // Panda Antivirus.
     99   L"tfwah.dll",                   // Threatfire (PC tools).
    100   L"wblind.dll",                  // Stardock Object desktop.
    101   L"wbhelp.dll",                  // Stardock Object desktop.
    102   L"winstylerthemehelper.dll"     // Tuneup utilities 2006.
    103 };
    104 
    105 // Adds the policy rules for the path and path\ with the semantic |access|.
    106 // If |children| is set to true, we need to add the wildcard rules to also
    107 // apply the rule to the subfiles and subfolders.
    108 bool AddDirectory(int path, const wchar_t* sub_dir, bool children,
    109                   sandbox::TargetPolicy::Semantics access,
    110                   sandbox::TargetPolicy* policy) {
    111   base::FilePath directory;
    112   if (!PathService::Get(path, &directory))
    113     return false;
    114 
    115   if (sub_dir)
    116     directory = base::MakeAbsoluteFilePath(directory.Append(sub_dir));
    117 
    118   sandbox::ResultCode result;
    119   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access,
    120                            directory.value().c_str());
    121   if (result != sandbox::SBOX_ALL_OK)
    122     return false;
    123 
    124   std::wstring directory_str = directory.value() + L"\\";
    125   if (children)
    126     directory_str += L"*";
    127   // Otherwise, add the version of the path that ends with a separator.
    128 
    129   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access,
    130                            directory_str.c_str());
    131   if (result != sandbox::SBOX_ALL_OK)
    132     return false;
    133 
    134   return true;
    135 }
    136 
    137 // Adds the policy rules for the path and path\* with the semantic |access|.
    138 // We need to add the wildcard rules to also apply the rule to the subkeys.
    139 bool AddKeyAndSubkeys(std::wstring key,
    140                       sandbox::TargetPolicy::Semantics access,
    141                       sandbox::TargetPolicy* policy) {
    142   sandbox::ResultCode result;
    143   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access,
    144                            key.c_str());
    145   if (result != sandbox::SBOX_ALL_OK)
    146     return false;
    147 
    148   key += L"\\*";
    149   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, access,
    150                            key.c_str());
    151   if (result != sandbox::SBOX_ALL_OK)
    152     return false;
    153 
    154   return true;
    155 }
    156 
    157 // Compares the loaded |module| file name matches |module_name|.
    158 bool IsExpandedModuleName(HMODULE module, const wchar_t* module_name) {
    159   wchar_t path[MAX_PATH];
    160   DWORD sz = ::GetModuleFileNameW(module, path, arraysize(path));
    161   if ((sz == arraysize(path)) || (sz == 0)) {
    162     // XP does not set the last error properly, so we bail out anyway.
    163     return false;
    164   }
    165   if (!::GetLongPathName(path, path, arraysize(path)))
    166     return false;
    167   base::FilePath fname(path);
    168   return (fname.BaseName().value() == module_name);
    169 }
    170 
    171 // Adds a single dll by |module_name| into the |policy| blacklist.
    172 // If |check_in_browser| is true we only add an unload policy only if the dll
    173 // is also loaded in this process.
    174 void BlacklistAddOneDll(const wchar_t* module_name,
    175                         bool check_in_browser,
    176                         sandbox::TargetPolicy* policy) {
    177   HMODULE module = check_in_browser ? ::GetModuleHandleW(module_name) : NULL;
    178   if (!module) {
    179     // The module could have been loaded with a 8.3 short name. We check
    180     // the three most common cases: 'thelongname.dll' becomes
    181     // 'thelon~1.dll', 'thelon~2.dll' and 'thelon~3.dll'.
    182     std::wstring name(module_name);
    183     size_t period = name.rfind(L'.');
    184     DCHECK_NE(std::string::npos, period);
    185     DCHECK_LE(3U, (name.size() - period));
    186     if (period <= 8)
    187       return;
    188     for (int ix = 0; ix < 3; ++ix) {
    189       const wchar_t suffix[] = {'~', ('1' + ix), 0};
    190       std::wstring alt_name = name.substr(0, 6) + suffix;
    191       alt_name += name.substr(period, name.size());
    192       if (check_in_browser) {
    193         module = ::GetModuleHandleW(alt_name.c_str());
    194         if (!module)
    195           return;
    196         // We found it, but because it only has 6 significant letters, we
    197         // want to make sure it is the right one.
    198         if (!IsExpandedModuleName(module, module_name))
    199           return;
    200       }
    201       // Found a match. We add both forms to the policy.
    202       policy->AddDllToUnload(alt_name.c_str());
    203     }
    204   }
    205   policy->AddDllToUnload(module_name);
    206   DVLOG(1) << "dll to unload found: " << module_name;
    207   return;
    208 }
    209 
    210 // Adds policy rules for unloaded the known dlls that cause chrome to crash.
    211 // Eviction of injected DLLs is done by the sandbox so that the injected module
    212 // does not get a chance to execute any code.
    213 void AddGenericDllEvictionPolicy(sandbox::TargetPolicy* policy) {
    214   for (int ix = 0; ix != arraysize(kTroublesomeDlls); ++ix)
    215     BlacklistAddOneDll(kTroublesomeDlls[ix], true, policy);
    216 }
    217 
    218 // Returns the object path prepended with the current logon session.
    219 string16 PrependWindowsSessionPath(const char16* object) {
    220   // Cache this because it can't change after process creation.
    221   static uintptr_t s_session_id = 0;
    222   if (s_session_id == 0) {
    223     HANDLE token;
    224     DWORD session_id_length;
    225     DWORD session_id = 0;
    226 
    227     CHECK(::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token));
    228     CHECK(::GetTokenInformation(token, TokenSessionId, &session_id,
    229         sizeof(session_id), &session_id_length));
    230     CloseHandle(token);
    231     if (session_id)
    232       s_session_id = session_id;
    233   }
    234 
    235   return base::StringPrintf(L"\\Sessions\\%d%ls", s_session_id, object);
    236 }
    237 
    238 // Checks if the sandbox should be let to run without a job object assigned.
    239 bool ShouldSetJobLevel(const CommandLine& cmd_line) {
    240   if (!cmd_line.HasSwitch(switches::kAllowNoSandboxJob))
    241     return true;
    242 
    243   // Windows 8 allows nested jobs so we don't need to check if we are in other
    244   // job.
    245   if (base::win::GetVersion() >= base::win::VERSION_WIN8)
    246     return true;
    247 
    248   BOOL in_job = true;
    249   // Either there is no job yet associated so we must add our job,
    250   if (!::IsProcessInJob(::GetCurrentProcess(), NULL, &in_job))
    251     NOTREACHED() << "IsProcessInJob failed. " << GetLastError();
    252   if (!in_job)
    253     return true;
    254 
    255   // ...or there is a job but the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit is set.
    256   JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0};
    257   if (!::QueryInformationJobObject(NULL,
    258                                    JobObjectExtendedLimitInformation, &job_info,
    259                                    sizeof(job_info), NULL)) {
    260     NOTREACHED() << "QueryInformationJobObject failed. " << GetLastError();
    261     return true;
    262   }
    263   if (job_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK)
    264     return true;
    265 
    266   return false;
    267 }
    268 
    269 // Adds the generic policy rules to a sandbox TargetPolicy.
    270 bool AddGenericPolicy(sandbox::TargetPolicy* policy) {
    271   sandbox::ResultCode result;
    272 
    273   // Renderers need to copy sections for plugin DIBs and GPU.
    274   // GPU needs to copy sections to renderers.
    275   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
    276                            sandbox::TargetPolicy::HANDLES_DUP_ANY,
    277                            L"Section");
    278   if (result != sandbox::SBOX_ALL_OK)
    279     return false;
    280 
    281   // Add the policy for the client side of a pipe. It is just a file
    282   // in the \pipe\ namespace. We restrict it to pipes that start with
    283   // "chrome." so the sandboxed process cannot connect to system services.
    284   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
    285                            sandbox::TargetPolicy::FILES_ALLOW_ANY,
    286                            L"\\??\\pipe\\chrome.*");
    287   if (result != sandbox::SBOX_ALL_OK)
    288     return false;
    289 
    290   // Add the policy for the server side of nacl pipe. It is just a file
    291   // in the \pipe\ namespace. We restrict it to pipes that start with
    292   // "chrome.nacl" so the sandboxed process cannot connect to
    293   // system services.
    294   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
    295                            sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
    296                            L"\\\\.\\pipe\\chrome.nacl.*");
    297   if (result != sandbox::SBOX_ALL_OK)
    298     return false;
    299 
    300   // Allow the server side of sync sockets, which are pipes that have
    301   // the "chrome.sync" namespace and a randomly generated suffix.
    302   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
    303                            sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
    304                            L"\\\\.\\pipe\\chrome.sync.*");
    305   if (result != sandbox::SBOX_ALL_OK)
    306     return false;
    307 
    308   // Add the policy for debug message only in debug
    309 #ifndef NDEBUG
    310   base::FilePath app_dir;
    311   if (!PathService::Get(base::DIR_MODULE, &app_dir))
    312     return false;
    313 
    314   wchar_t long_path_buf[MAX_PATH];
    315   DWORD long_path_return_value = GetLongPathName(app_dir.value().c_str(),
    316                                                  long_path_buf,
    317                                                  MAX_PATH);
    318   if (long_path_return_value == 0 || long_path_return_value >= MAX_PATH)
    319     return false;
    320 
    321   base::FilePath debug_message(long_path_buf);
    322   debug_message = debug_message.AppendASCII("debug_message.exe");
    323   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_PROCESS,
    324                            sandbox::TargetPolicy::PROCESS_MIN_EXEC,
    325                            debug_message.value().c_str());
    326   if (result != sandbox::SBOX_ALL_OK)
    327     return false;
    328 #endif  // NDEBUG
    329 
    330   AddGenericDllEvictionPolicy(policy);
    331 
    332   return true;
    333 }
    334 
    335 bool AddPolicyForSandboxedProcess(sandbox::TargetPolicy* policy) {
    336   sandbox::ResultCode result;
    337   // Renderers need to share events with plugins.
    338   result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
    339                            sandbox::TargetPolicy::HANDLES_DUP_ANY,
    340                            L"Event");
    341   if (result != sandbox::SBOX_ALL_OK)
    342     return false;
    343 
    344   sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED;
    345   if (base::win::GetVersion() > base::win::VERSION_XP) {
    346     // On 2003/Vista the initial token has to be restricted if the main
    347     // token is restricted.
    348     initial_token = sandbox::USER_RESTRICTED_SAME_ACCESS;
    349   }
    350 
    351   policy->SetTokenLevel(initial_token, sandbox::USER_LOCKDOWN);
    352   // Prevents the renderers from manipulating low-integrity processes.
    353   policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
    354 
    355   bool use_winsta = !CommandLine::ForCurrentProcess()->HasSwitch(
    356                         switches::kDisableAltWinstation);
    357 
    358   if (sandbox::SBOX_ALL_OK !=  policy->SetAlternateDesktop(use_winsta)) {
    359     DLOG(WARNING) << "Failed to apply desktop security to the renderer";
    360   }
    361 
    362   return true;
    363 }
    364 
    365 // Updates the command line arguments with debug-related flags. If debug flags
    366 // have been used with this process, they will be filtered and added to
    367 // command_line as needed. is_in_sandbox must be true if the child process will
    368 // be in a sandbox.
    369 //
    370 // Returns true if the caller should "help" the child process by calling the JIT
    371 // debugger on it. It may only happen if is_in_sandbox is true.
    372 bool ProcessDebugFlags(CommandLine* command_line, bool is_in_sandbox) {
    373   bool should_help_child = false;
    374   const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
    375   std::string type = command_line->GetSwitchValueASCII(switches::kProcessType);
    376   if (current_cmd_line.HasSwitch(switches::kDebugChildren)) {
    377     // Look to pass-on the kDebugOnStart flag.
    378     std::string value = current_cmd_line.GetSwitchValueASCII(
    379         switches::kDebugChildren);
    380     if (value.empty() || value == type) {
    381       command_line->AppendSwitch(switches::kDebugOnStart);
    382       should_help_child = true;
    383     }
    384     command_line->AppendSwitchASCII(switches::kDebugChildren, value);
    385   } else if (current_cmd_line.HasSwitch(switches::kWaitForDebuggerChildren)) {
    386     // Look to pass-on the kWaitForDebugger flag.
    387     std::string value = current_cmd_line.GetSwitchValueASCII(
    388         switches::kWaitForDebuggerChildren);
    389     if (value.empty() || value == type) {
    390       command_line->AppendSwitch(switches::kWaitForDebugger);
    391     }
    392     command_line->AppendSwitchASCII(switches::kWaitForDebuggerChildren, value);
    393   }
    394   return should_help_child;
    395 }
    396 
    397 // This code is test only, and attempts to catch unsafe uses of
    398 // DuplicateHandle() that copy privileged handles into sandboxed processes.
    399 #ifndef OFFICIAL_BUILD
    400 base::win::IATPatchFunction g_iat_patch_duplicate_handle;
    401 
    402 BOOL (WINAPI *g_iat_orig_duplicate_handle)(HANDLE source_process_handle,
    403                                            HANDLE source_handle,
    404                                            HANDLE target_process_handle,
    405                                            LPHANDLE target_handle,
    406                                            DWORD desired_access,
    407                                            BOOL inherit_handle,
    408                                            DWORD options);
    409 
    410 NtQueryObject g_QueryObject = NULL;
    411 
    412 static const char* kDuplicateHandleWarning =
    413     "You are attempting to duplicate a privileged handle into a sandboxed"
    414     " process.\n Please use the sandbox::BrokerDuplicateHandle API or"
    415     " contact security (at) chromium.org for assistance.";
    416 
    417 void CheckDuplicateHandle(HANDLE handle) {
    418   // Get the object type (32 characters is safe; current max is 14).
    419   BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)];
    420   OBJECT_TYPE_INFORMATION* type_info =
    421       reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer);
    422   ULONG size = sizeof(buffer) - sizeof(wchar_t);
    423   NTSTATUS error;
    424   error = g_QueryObject(handle, ObjectTypeInformation, type_info, size, &size);
    425   CHECK(NT_SUCCESS(error));
    426   type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
    427 
    428   // Get the object basic information.
    429   OBJECT_BASIC_INFORMATION basic_info;
    430   size = sizeof(basic_info);
    431   error = g_QueryObject(handle, ObjectBasicInformation, &basic_info, size,
    432                         &size);
    433   CHECK(NT_SUCCESS(error));
    434 
    435   if (0 == _wcsicmp(type_info->Name.Buffer, L"Process")) {
    436     const ACCESS_MASK kDangerousMask = ~(PROCESS_QUERY_LIMITED_INFORMATION |
    437                                          SYNCHRONIZE);
    438     CHECK(!(basic_info.GrantedAccess & kDangerousMask)) <<
    439         kDuplicateHandleWarning;
    440   }
    441 }
    442 
    443 BOOL WINAPI DuplicateHandlePatch(HANDLE source_process_handle,
    444                                  HANDLE source_handle,
    445                                  HANDLE target_process_handle,
    446                                  LPHANDLE target_handle,
    447                                  DWORD desired_access,
    448                                  BOOL inherit_handle,
    449                                  DWORD options) {
    450   // Duplicate the handle so we get the final access mask.
    451   if (!g_iat_orig_duplicate_handle(source_process_handle, source_handle,
    452                                    target_process_handle, target_handle,
    453                                    desired_access, inherit_handle, options))
    454     return FALSE;
    455 
    456   // We're not worried about broker handles or not crossing process boundaries.
    457   if (source_process_handle == target_process_handle ||
    458       target_process_handle == ::GetCurrentProcess())
    459     return TRUE;
    460 
    461   // Only sandboxed children are placed in jobs, so just check them.
    462   BOOL is_in_job = FALSE;
    463   if (!::IsProcessInJob(target_process_handle, NULL, &is_in_job)) {
    464     // We need a handle with permission to check the job object.
    465     if (ERROR_ACCESS_DENIED == ::GetLastError()) {
    466       base::win::ScopedHandle process;
    467       CHECK(g_iat_orig_duplicate_handle(::GetCurrentProcess(),
    468                                         target_process_handle,
    469                                         ::GetCurrentProcess(),
    470                                         process.Receive(),
    471                                         PROCESS_QUERY_INFORMATION,
    472                                         FALSE, 0));
    473       CHECK(::IsProcessInJob(process, NULL, &is_in_job));
    474     }
    475   }
    476 
    477   if (is_in_job) {
    478     // We never allow inheritable child handles.
    479     CHECK(!inherit_handle) << kDuplicateHandleWarning;
    480 
    481     // Duplicate the handle again, to get the final permissions.
    482     base::win::ScopedHandle handle;
    483     CHECK(g_iat_orig_duplicate_handle(target_process_handle, *target_handle,
    484                                       ::GetCurrentProcess(), handle.Receive(),
    485                                       0, FALSE, DUPLICATE_SAME_ACCESS));
    486 
    487     // Callers use CHECK macro to make sure we get the right stack.
    488     CheckDuplicateHandle(handle);
    489   }
    490 
    491   return TRUE;
    492 }
    493 #endif
    494 
    495 }  // namespace
    496 
    497 void SetJobLevel(const CommandLine& cmd_line,
    498                  sandbox::JobLevel job_level,
    499                  uint32 ui_exceptions,
    500                  sandbox::TargetPolicy* policy) {
    501   if (ShouldSetJobLevel(cmd_line))
    502     policy->SetJobLevel(job_level, ui_exceptions);
    503   else
    504     policy->SetJobLevel(sandbox::JOB_NONE, 0);
    505 }
    506 
    507 // TODO(jschuh): Need get these restrictions applied to NaCl and Pepper.
    508 // Just have to figure out what needs to be warmed up first.
    509 void AddBaseHandleClosePolicy(sandbox::TargetPolicy* policy) {
    510   // Being able to manipulate anything BaseNamedObjects is bad.
    511   string16 object_path = PrependWindowsSessionPath(L"\\BaseNamedObjects");
    512   policy->AddKernelObjectToClose(L"Directory", object_path.data());
    513   object_path = PrependWindowsSessionPath(
    514       L"\\BaseNamedObjects\\windows_shell_global_counters");
    515   policy->AddKernelObjectToClose(L"Section", object_path.data());
    516 }
    517 
    518 bool InitBrokerServices(sandbox::BrokerServices* broker_services) {
    519   // TODO(abarth): DCHECK(CalledOnValidThread());
    520   //               See <http://b/1287166>.
    521   DCHECK(broker_services);
    522   DCHECK(!g_broker_services);
    523   sandbox::ResultCode result = broker_services->Init();
    524   g_broker_services = broker_services;
    525 
    526   // In non-official builds warn about dangerous uses of DuplicateHandle.
    527 #ifndef OFFICIAL_BUILD
    528   BOOL is_in_job = FALSE;
    529   CHECK(::IsProcessInJob(::GetCurrentProcess(), NULL, &is_in_job));
    530   // In a Syzygy-profiled binary, instrumented for import profiling, this
    531   // patch will end in infinite recursion on the attempted delegation to the
    532   // original function.
    533   if (!base::debug::IsBinaryInstrumented() &&
    534       !is_in_job && !g_iat_patch_duplicate_handle.is_patched()) {
    535     HMODULE module = NULL;
    536     wchar_t module_name[MAX_PATH];
    537     CHECK(::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
    538                               reinterpret_cast<LPCWSTR>(InitBrokerServices),
    539                               &module));
    540     DWORD result = ::GetModuleFileNameW(module, module_name, MAX_PATH);
    541     if (result && (result != MAX_PATH)) {
    542       ResolveNTFunctionPtr("NtQueryObject", &g_QueryObject);
    543       g_iat_orig_duplicate_handle = ::DuplicateHandle;
    544       g_iat_patch_duplicate_handle.Patch(
    545           module_name, "kernel32.dll", "DuplicateHandle",
    546           DuplicateHandlePatch);
    547     }
    548   }
    549 #endif
    550 
    551   return sandbox::SBOX_ALL_OK == result;
    552 }
    553 
    554 bool InitTargetServices(sandbox::TargetServices* target_services) {
    555   DCHECK(target_services);
    556   DCHECK(!g_target_services);
    557   sandbox::ResultCode result = target_services->Init();
    558   g_target_services = target_services;
    559   return sandbox::SBOX_ALL_OK == result;
    560 }
    561 
    562 base::ProcessHandle StartSandboxedProcess(
    563     SandboxedProcessLauncherDelegate* delegate,
    564     CommandLine* cmd_line) {
    565   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
    566   std::string type_str = cmd_line->GetSwitchValueASCII(switches::kProcessType);
    567 
    568   TRACE_EVENT_BEGIN_ETW("StartProcessWithAccess", 0, type_str);
    569 
    570   bool in_sandbox = true;
    571   if (delegate)
    572     delegate->ShouldSandbox(&in_sandbox);
    573 
    574   if (browser_command_line.HasSwitch(switches::kNoSandbox) ||
    575       cmd_line->HasSwitch(switches::kNoSandbox)) {
    576     // The user or the caller has explicity opted-out from all sandboxing.
    577     in_sandbox = false;
    578   }
    579 
    580 
    581   // Propagate the --allow-no-job flag if present.
    582   if (browser_command_line.HasSwitch(switches::kAllowNoSandboxJob) &&
    583       !cmd_line->HasSwitch(switches::kAllowNoSandboxJob)) {
    584     cmd_line->AppendSwitch(switches::kAllowNoSandboxJob);
    585   }
    586 
    587   bool child_needs_help = ProcessDebugFlags(cmd_line, in_sandbox);
    588 
    589   // Prefetch hints on windows:
    590   // Using a different prefetch profile per process type will allow Windows
    591   // to create separate pretetch settings for browser, renderer etc.
    592   cmd_line->AppendArg(base::StringPrintf("/prefetch:%d", base::Hash(type_str)));
    593 
    594   if (!in_sandbox) {
    595     base::ProcessHandle process = 0;
    596     base::LaunchProcess(*cmd_line, base::LaunchOptions(), &process);
    597     g_broker_services->AddTargetPeer(process);
    598     return process;
    599   }
    600 
    601   base::win::ScopedProcessInformation target;
    602   sandbox::TargetPolicy* policy = g_broker_services->CreatePolicy();
    603 
    604   sandbox::MitigationFlags mitigations = sandbox::MITIGATION_HEAP_TERMINATE |
    605                                          sandbox::MITIGATION_BOTTOM_UP_ASLR |
    606                                          sandbox::MITIGATION_DEP |
    607                                          sandbox::MITIGATION_DEP_NO_ATL_THUNK |
    608                                          sandbox::MITIGATION_SEHOP;
    609 
    610   if (policy->SetProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK)
    611     return 0;
    612 
    613   mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
    614                 sandbox::MITIGATION_DLL_SEARCH_ORDER;
    615 
    616   if (policy->SetDelayedProcessMitigations(mitigations) != sandbox::SBOX_ALL_OK)
    617     return 0;
    618 
    619   SetJobLevel(*cmd_line, sandbox::JOB_LOCKDOWN, 0, policy);
    620 
    621   bool disable_default_policy = false;
    622   base::FilePath exposed_dir;
    623   if (delegate)
    624     delegate->PreSandbox(&disable_default_policy, &exposed_dir);
    625 
    626   if (!disable_default_policy && !AddPolicyForSandboxedProcess(policy))
    627     return 0;
    628 
    629   if (type_str != switches::kRendererProcess) {
    630     // Hack for Google Desktop crash. Trick GD into not injecting its DLL into
    631     // this subprocess. See
    632     // http://code.google.com/p/chromium/issues/detail?id=25580
    633     cmd_line->AppendSwitchASCII("ignored", " --type=renderer ");
    634   }
    635 
    636   sandbox::ResultCode result;
    637   if (!exposed_dir.empty()) {
    638     result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
    639                              sandbox::TargetPolicy::FILES_ALLOW_ANY,
    640                              exposed_dir.value().c_str());
    641     if (result != sandbox::SBOX_ALL_OK)
    642       return 0;
    643 
    644     base::FilePath exposed_files = exposed_dir.AppendASCII("*");
    645     result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
    646                              sandbox::TargetPolicy::FILES_ALLOW_ANY,
    647                              exposed_files.value().c_str());
    648     if (result != sandbox::SBOX_ALL_OK)
    649       return 0;
    650   }
    651 
    652   if (!AddGenericPolicy(policy)) {
    653     NOTREACHED();
    654     return 0;
    655   }
    656 
    657   if (browser_command_line.HasSwitch(switches::kEnableLogging)) {
    658     // If stdout/stderr point to a Windows console, these calls will
    659     // have no effect.
    660     policy->SetStdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE));
    661     policy->SetStderrHandle(GetStdHandle(STD_ERROR_HANDLE));
    662   }
    663 
    664   if (delegate) {
    665     bool success = true;
    666     delegate->PreSpawnTarget(policy, &success);
    667     if (!success)
    668       return 0;
    669   }
    670 
    671   TRACE_EVENT_BEGIN_ETW("StartProcessWithAccess::LAUNCHPROCESS", 0, 0);
    672 
    673   result = g_broker_services->SpawnTarget(
    674       cmd_line->GetProgram().value().c_str(),
    675       cmd_line->GetCommandLineString().c_str(),
    676       policy, target.Receive());
    677   policy->Release();
    678 
    679   TRACE_EVENT_END_ETW("StartProcessWithAccess::LAUNCHPROCESS", 0, 0);
    680 
    681   if (sandbox::SBOX_ALL_OK != result) {
    682     if (result == sandbox::SBOX_ERROR_GENERIC)
    683       DPLOG(ERROR) << "Failed to launch process";
    684     else
    685       DLOG(ERROR) << "Failed to launch process. Error: " << result;
    686     return 0;
    687   }
    688 
    689   if (delegate)
    690     delegate->PostSpawnTarget(target.process_handle());
    691 
    692   ResumeThread(target.thread_handle());
    693 
    694   // Help the process a little. It can't start the debugger by itself if
    695   // the process is in a sandbox.
    696   if (child_needs_help)
    697     base::debug::SpawnDebuggerOnProcess(target.process_id());
    698 
    699   return target.TakeProcessHandle();
    700 }
    701 
    702 bool BrokerDuplicateHandle(HANDLE source_handle,
    703                            DWORD target_process_id,
    704                            HANDLE* target_handle,
    705                            DWORD desired_access,
    706                            DWORD options) {
    707   // If our process is the target just duplicate the handle.
    708   if (::GetCurrentProcessId() == target_process_id) {
    709     return !!::DuplicateHandle(::GetCurrentProcess(), source_handle,
    710                                ::GetCurrentProcess(), target_handle,
    711                                desired_access, FALSE, options);
    712 
    713   }
    714 
    715   // Try the broker next
    716   if (g_target_services &&
    717       g_target_services->DuplicateHandle(source_handle, target_process_id,
    718                                          target_handle, desired_access,
    719                                          options) == sandbox::SBOX_ALL_OK) {
    720     return true;
    721   }
    722 
    723   // Finally, see if we already have access to the process.
    724   base::win::ScopedHandle target_process;
    725   target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE,
    726                                     target_process_id));
    727   if (target_process.IsValid()) {
    728     return !!::DuplicateHandle(::GetCurrentProcess(), source_handle,
    729                                 target_process, target_handle,
    730                                 desired_access, FALSE, options);
    731   }
    732 
    733   return false;
    734 }
    735 
    736 bool BrokerAddTargetPeer(HANDLE peer_process) {
    737   return g_broker_services->AddTargetPeer(peer_process) == sandbox::SBOX_ALL_OK;
    738 }
    739 
    740 }  // namespace content
    741