Home | History | Annotate | Download | only in src
      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 "sandbox/win/src/target_process.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/win/pe_image.h"
     10 #include "base/win/startup_information.h"
     11 #include "base/win/windows_version.h"
     12 #include "sandbox/win/src/crosscall_server.h"
     13 #include "sandbox/win/src/crosscall_client.h"
     14 #include "sandbox/win/src/policy_low_level.h"
     15 #include "sandbox/win/src/sandbox_types.h"
     16 #include "sandbox/win/src/sharedmem_ipc_server.h"
     17 
     18 namespace {
     19 
     20 void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
     21   if (!source || !size)
     22     return;
     23   memcpy(dest, source, size);
     24   sandbox::PolicyGlobal* policy =
     25       reinterpret_cast<sandbox::PolicyGlobal*>(dest);
     26 
     27   size_t offset = reinterpret_cast<size_t>(source);
     28 
     29   for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
     30     size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
     31     if (buffer) {
     32       buffer -= offset;
     33       policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
     34     }
     35   }
     36 }
     37 
     38 }
     39 
     40 namespace sandbox {
     41 
     42 SANDBOX_INTERCEPT HANDLE g_shared_section;
     43 SANDBOX_INTERCEPT size_t g_shared_IPC_size;
     44 SANDBOX_INTERCEPT size_t g_shared_policy_size;
     45 
     46 // Returns the address of the main exe module in memory taking in account
     47 // address space layout randomization.
     48 void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) {
     49   HMODULE exe = ::LoadLibrary(exe_name);
     50   if (NULL == exe)
     51     return exe;
     52 
     53   base::win::PEImage pe(exe);
     54   if (!pe.VerifyMagic()) {
     55     ::FreeLibrary(exe);
     56     return exe;
     57   }
     58   PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders();
     59   char* base = reinterpret_cast<char*>(entry_point) -
     60     nt_header->OptionalHeader.AddressOfEntryPoint;
     61 
     62   ::FreeLibrary(exe);
     63   return base;
     64 }
     65 
     66 
     67 TargetProcess::TargetProcess(HANDLE initial_token, HANDLE lockdown_token,
     68                              HANDLE job, ThreadProvider* thread_pool)
     69   // This object owns everything initialized here except thread_pool and
     70   // the job_ handle. The Job handle is closed by BrokerServices and results
     71   // eventually in a call to our dtor.
     72     : lockdown_token_(lockdown_token),
     73       initial_token_(initial_token),
     74       job_(job),
     75       thread_pool_(thread_pool),
     76       base_address_(NULL) {
     77 }
     78 
     79 TargetProcess::~TargetProcess() {
     80   DWORD exit_code = 0;
     81   // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
     82   // will take effect only when the context changes. As far as the testing went,
     83   // this wait was enough to switch context and kill the processes in the job.
     84   // If this process is already dead, the function will return without waiting.
     85   // TODO(nsylvain):  If the process is still alive at the end, we should kill
     86   // it. http://b/893891
     87   // For now, this wait is there only to do a best effort to prevent some leaks
     88   // from showing up in purify.
     89   if (sandbox_process_info_.IsValid()) {
     90     ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
     91     if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(),
     92                               &exit_code) || (STILL_ACTIVE == exit_code)) {
     93       // It is an error to destroy this object while the target process is still
     94       // alive because we need to destroy the IPC subsystem and cannot risk to
     95       // have an IPC reach us after this point.
     96       if (shared_section_.IsValid())
     97         shared_section_.Take();
     98       SharedMemIPCServer* server = ipc_server_.release();
     99       sandbox_process_info_.TakeProcessHandle();
    100       return;
    101     }
    102   }
    103 
    104   // ipc_server_ references our process handle, so make sure the former is shut
    105   // down before the latter is closed (by ScopedProcessInformation).
    106   ipc_server_.reset();
    107 }
    108 
    109 // Creates the target (child) process suspended and assigns it to the job
    110 // object.
    111 DWORD TargetProcess::Create(const wchar_t* exe_path,
    112                             const wchar_t* command_line,
    113                             bool inherit_handles,
    114                             const base::win::StartupInformation& startup_info,
    115                             base::win::ScopedProcessInformation* target_info) {
    116   exe_name_.reset(_wcsdup(exe_path));
    117 
    118   // the command line needs to be writable by CreateProcess().
    119   scoped_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
    120 
    121   // Start the target process suspended.
    122   DWORD flags =
    123       CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
    124 
    125   if (startup_info.has_extended_startup_info())
    126     flags |= EXTENDED_STARTUPINFO_PRESENT;
    127 
    128   if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) {
    129     // Windows 8 implements nested jobs, but for older systems we need to
    130     // break out of any job we're in to enforce our restrictions.
    131     flags |= CREATE_BREAKAWAY_FROM_JOB;
    132   }
    133 
    134   PROCESS_INFORMATION temp_process_info = {};
    135   if (!::CreateProcessAsUserW(lockdown_token_,
    136                               exe_path,
    137                               cmd_line.get(),
    138                               NULL,   // No security attribute.
    139                               NULL,   // No thread attribute.
    140                               inherit_handles,
    141                               flags,
    142                               NULL,   // Use the environment of the caller.
    143                               NULL,   // Use current directory of the caller.
    144                               startup_info.startup_info(),
    145                               &temp_process_info)) {
    146     return ::GetLastError();
    147   }
    148   base::win::ScopedProcessInformation process_info(temp_process_info);
    149   lockdown_token_.Close();
    150 
    151   DWORD win_result = ERROR_SUCCESS;
    152 
    153   if (job_) {
    154     // Assign the suspended target to the windows job object.
    155     if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
    156       win_result = ::GetLastError();
    157       ::TerminateProcess(process_info.process_handle(), 0);
    158       return win_result;
    159     }
    160   }
    161 
    162   if (initial_token_.IsValid()) {
    163     // Change the token of the main thread of the new process for the
    164     // impersonation token with more rights. This allows the target to start;
    165     // otherwise it will crash too early for us to help.
    166     HANDLE temp_thread = process_info.thread_handle();
    167     if (!::SetThreadToken(&temp_thread, initial_token_)) {
    168       win_result = ::GetLastError();
    169       // It might be a security breach if we let the target run outside the job
    170       // so kill it before it causes damage.
    171       ::TerminateProcess(process_info.process_handle(), 0);
    172       return win_result;
    173     }
    174     initial_token_.Close();
    175   }
    176 
    177   CONTEXT context;
    178   context.ContextFlags = CONTEXT_ALL;
    179   if (!::GetThreadContext(process_info.thread_handle(), &context)) {
    180     win_result = ::GetLastError();
    181     ::TerminateProcess(process_info.process_handle(), 0);
    182     return win_result;
    183   }
    184 
    185 #if defined(_WIN64)
    186   void* entry_point = reinterpret_cast<void*>(context.Rcx);
    187 #else
    188 #pragma warning(push)
    189 #pragma warning(disable: 4312)
    190   // This cast generates a warning because it is 32 bit specific.
    191   void* entry_point = reinterpret_cast<void*>(context.Eax);
    192 #pragma warning(pop)
    193 #endif  // _WIN64
    194 
    195   if (!target_info->DuplicateFrom(process_info)) {
    196     win_result = ::GetLastError();  // This may or may not be correct.
    197     ::TerminateProcess(process_info.process_handle(), 0);
    198     return win_result;
    199   }
    200 
    201   base_address_ = GetBaseAddress(exe_path, entry_point);
    202   sandbox_process_info_.Set(process_info.Take());
    203   return win_result;
    204 }
    205 
    206 ResultCode TargetProcess::TransferVariable(const char* name, void* address,
    207                                            size_t size) {
    208   if (!sandbox_process_info_.IsValid())
    209     return SBOX_ERROR_UNEXPECTED_CALL;
    210 
    211   void* child_var = address;
    212 
    213 #if SANDBOX_EXPORTS
    214   HMODULE module = ::LoadLibrary(exe_name_.get());
    215   if (NULL == module)
    216     return SBOX_ERROR_GENERIC;
    217 
    218   child_var = ::GetProcAddress(module, name);
    219   ::FreeLibrary(module);
    220 
    221   if (NULL == child_var)
    222     return SBOX_ERROR_GENERIC;
    223 
    224   size_t offset = reinterpret_cast<char*>(child_var) -
    225                   reinterpret_cast<char*>(module);
    226   child_var = reinterpret_cast<char*>(MainModule()) + offset;
    227 #else
    228   UNREFERENCED_PARAMETER(name);
    229 #endif
    230 
    231   SIZE_T written;
    232   if (!::WriteProcessMemory(sandbox_process_info_.process_handle(),
    233                             child_var, address, size, &written))
    234     return SBOX_ERROR_GENERIC;
    235 
    236   if (written != size)
    237     return SBOX_ERROR_GENERIC;
    238 
    239   return SBOX_ALL_OK;
    240 }
    241 
    242 // Construct the IPC server and the IPC dispatcher. When the target does
    243 // an IPC it will eventually call the dispatcher.
    244 DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy,
    245                           uint32 shared_IPC_size, uint32 shared_policy_size) {
    246   // We need to map the shared memory on the target. This is necessary for
    247   // any IPC that needs to take place, even if the target has not yet hit
    248   // the main( ) function or even has initialized the CRT. So here we set
    249   // the handle to the shared section. The target on the first IPC must do
    250   // the rest, which boils down to calling MapViewofFile()
    251 
    252   // We use this single memory pool for IPC and for policy.
    253   DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size +
    254                                              shared_policy_size);
    255   shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
    256                                            PAGE_READWRITE | SEC_COMMIT,
    257                                            0, shared_mem_size, NULL));
    258   if (!shared_section_.IsValid()) {
    259     return ::GetLastError();
    260   }
    261 
    262   DWORD access = FILE_MAP_READ | FILE_MAP_WRITE;
    263   HANDLE target_shared_section;
    264   if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_,
    265                          sandbox_process_info_.process_handle(),
    266                          &target_shared_section, access, FALSE, 0)) {
    267     return ::GetLastError();
    268   }
    269 
    270   void* shared_memory = ::MapViewOfFile(shared_section_,
    271                                         FILE_MAP_WRITE|FILE_MAP_READ,
    272                                         0, 0, 0);
    273   if (NULL == shared_memory) {
    274     return ::GetLastError();
    275   }
    276 
    277   CopyPolicyToTarget(policy, shared_policy_size,
    278                      reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
    279 
    280   ResultCode ret;
    281   // Set the global variables in the target. These are not used on the broker.
    282   g_shared_section = target_shared_section;
    283   ret = TransferVariable("g_shared_section", &g_shared_section,
    284                          sizeof(g_shared_section));
    285   g_shared_section = NULL;
    286   if (SBOX_ALL_OK != ret) {
    287     return (SBOX_ERROR_GENERIC == ret)?
    288            ::GetLastError() : ERROR_INVALID_FUNCTION;
    289   }
    290   g_shared_IPC_size = shared_IPC_size;
    291   ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
    292                          sizeof(g_shared_IPC_size));
    293   g_shared_IPC_size = 0;
    294   if (SBOX_ALL_OK != ret) {
    295     return (SBOX_ERROR_GENERIC == ret) ?
    296            ::GetLastError() : ERROR_INVALID_FUNCTION;
    297   }
    298   g_shared_policy_size = shared_policy_size;
    299   ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size,
    300                          sizeof(g_shared_policy_size));
    301   g_shared_policy_size = 0;
    302   if (SBOX_ALL_OK != ret) {
    303     return (SBOX_ERROR_GENERIC == ret) ?
    304            ::GetLastError() : ERROR_INVALID_FUNCTION;
    305   }
    306 
    307   ipc_server_.reset(
    308       new SharedMemIPCServer(sandbox_process_info_.process_handle(),
    309                              sandbox_process_info_.process_id(),
    310                              job_, thread_pool_, ipc_dispatcher));
    311 
    312   if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
    313     return ERROR_NOT_ENOUGH_MEMORY;
    314 
    315   // After this point we cannot use this handle anymore.
    316   ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
    317 
    318   return ERROR_SUCCESS;
    319 }
    320 
    321 void TargetProcess::Terminate() {
    322   if (!sandbox_process_info_.IsValid())
    323     return;
    324 
    325   ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
    326 }
    327 
    328 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
    329   TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL);
    330   PROCESS_INFORMATION process_info = {};
    331   process_info.hProcess = process;
    332   target->sandbox_process_info_.Set(process_info);
    333   target->base_address_ = base_address;
    334   return target;
    335 }
    336 
    337 }  // namespace sandbox
    338