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