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