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