security/sandbox/win/src/target_process.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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.
     5 #include "sandbox/win/src/target_process.h"
     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"
    18 namespace {
    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);
    27   size_t offset = reinterpret_cast<size_t>(source);
    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 }
    38 }
    40 namespace sandbox {
    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;
    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;
    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;
    62   ::FreeLibrary(exe);
    63   return base;
    64 }
    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 }
    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   }
   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 }
   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));
   118   // the command line needs to be writable by CreateProcess().
   119   scoped_ptr_malloc<wchar_t> cmd_line(_wcsdup(command_line));
   121   // Start the target process suspended.
   122   DWORD flags =
   123       CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
   125   if (startup_info.has_extended_startup_info())
   126     flags |= EXTENDED_STARTUPINFO_PRESENT;
   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   }
   134   base::win::ScopedProcessInformation process_info;
   136   if (!::CreateProcessAsUserW(lockdown_token_,
   137                               exe_path,
   138                               cmd_line.get(),
   139                               NULL,   // No security attribute.
   140                               NULL,   // No thread attribute.
   141                               inherit_handles,
   142                               flags,
   143                               NULL,   // Use the environment of the caller.
   144                               NULL,   // Use current directory of the caller.
   145                               startup_info.startup_info(),
   146                               process_info.Receive())) {
   147     return ::GetLastError();
   148   }
   149   lockdown_token_.Close();
   151   DWORD win_result = ERROR_SUCCESS;
   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   }
   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   }
   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   }
   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
   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   }
   201   base_address_ = GetBaseAddress(exe_path, entry_point);
   202   sandbox_process_info_.Set(process_info.Take());
   203   return win_result;
   204 }
   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;
   211   void* child_var = address;
   213 #if SANDBOX_EXPORTS
   214   HMODULE module = ::LoadLibrary(exe_name_.get());
   215   if (NULL == module)
   216     return SBOX_ERROR_GENERIC;
   218   child_var = ::GetProcAddress(module, name);
   219   ::FreeLibrary(module);
   221   if (NULL == child_var)
   222     return SBOX_ERROR_GENERIC;
   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
   231   SIZE_T written;
   232   if (!::WriteProcessMemory(sandbox_process_info_.process_handle(),
   233                             child_var, address, size, &written))
   234     return SBOX_ERROR_GENERIC;
   236   if (written != size)
   237     return SBOX_ERROR_GENERIC;
   239   return SBOX_ALL_OK;
   240 }
   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()
   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   }
   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   }
   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   }
   277   CopyPolicyToTarget(policy, shared_policy_size,
   278                      reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
   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   }
   307   ipc_server_.reset(
   308       new SharedMemIPCServer(sandbox_process_info_.process_handle(),
   309                              sandbox_process_info_.process_id(),
   310                              job_, thread_pool_, ipc_dispatcher));
   312   if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
   313     return ERROR_NOT_ENOUGH_MEMORY;
   315   // After this point we cannot use this handle anymore.
   316   ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
   318   return ERROR_SUCCESS;
   319 }
   321 void TargetProcess::Terminate() {
   322   if (!sandbox_process_info_.IsValid())
   323     return;
   325   ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
   326 }
   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 }
   337 }  // namespace sandbox

mercurial