1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/target_process.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,337 @@ 1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "sandbox/win/src/target_process.h" 1.9 + 1.10 +#include "base/basictypes.h" 1.11 +#include "base/memory/scoped_ptr.h" 1.12 +#include "base/win/pe_image.h" 1.13 +#include "base/win/startup_information.h" 1.14 +#include "base/win/windows_version.h" 1.15 +#include "sandbox/win/src/crosscall_server.h" 1.16 +#include "sandbox/win/src/crosscall_client.h" 1.17 +#include "sandbox/win/src/policy_low_level.h" 1.18 +#include "sandbox/win/src/sandbox_types.h" 1.19 +#include "sandbox/win/src/sharedmem_ipc_server.h" 1.20 + 1.21 +namespace { 1.22 + 1.23 +void CopyPolicyToTarget(const void* source, size_t size, void* dest) { 1.24 + if (!source || !size) 1.25 + return; 1.26 + memcpy(dest, source, size); 1.27 + sandbox::PolicyGlobal* policy = 1.28 + reinterpret_cast<sandbox::PolicyGlobal*>(dest); 1.29 + 1.30 + size_t offset = reinterpret_cast<size_t>(source); 1.31 + 1.32 + for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) { 1.33 + size_t buffer = reinterpret_cast<size_t>(policy->entry[i]); 1.34 + if (buffer) { 1.35 + buffer -= offset; 1.36 + policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer); 1.37 + } 1.38 + } 1.39 +} 1.40 + 1.41 +} 1.42 + 1.43 +namespace sandbox { 1.44 + 1.45 +SANDBOX_INTERCEPT HANDLE g_shared_section; 1.46 +SANDBOX_INTERCEPT size_t g_shared_IPC_size; 1.47 +SANDBOX_INTERCEPT size_t g_shared_policy_size; 1.48 + 1.49 +// Returns the address of the main exe module in memory taking in account 1.50 +// address space layout randomization. 1.51 +void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) { 1.52 + HMODULE exe = ::LoadLibrary(exe_name); 1.53 + if (NULL == exe) 1.54 + return exe; 1.55 + 1.56 + base::win::PEImage pe(exe); 1.57 + if (!pe.VerifyMagic()) { 1.58 + ::FreeLibrary(exe); 1.59 + return exe; 1.60 + } 1.61 + PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders(); 1.62 + char* base = reinterpret_cast<char*>(entry_point) - 1.63 + nt_header->OptionalHeader.AddressOfEntryPoint; 1.64 + 1.65 + ::FreeLibrary(exe); 1.66 + return base; 1.67 +} 1.68 + 1.69 + 1.70 +TargetProcess::TargetProcess(HANDLE initial_token, HANDLE lockdown_token, 1.71 + HANDLE job, ThreadProvider* thread_pool) 1.72 + // This object owns everything initialized here except thread_pool and 1.73 + // the job_ handle. The Job handle is closed by BrokerServices and results 1.74 + // eventually in a call to our dtor. 1.75 + : lockdown_token_(lockdown_token), 1.76 + initial_token_(initial_token), 1.77 + job_(job), 1.78 + thread_pool_(thread_pool), 1.79 + base_address_(NULL) { 1.80 +} 1.81 + 1.82 +TargetProcess::~TargetProcess() { 1.83 + DWORD exit_code = 0; 1.84 + // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE 1.85 + // will take effect only when the context changes. As far as the testing went, 1.86 + // this wait was enough to switch context and kill the processes in the job. 1.87 + // If this process is already dead, the function will return without waiting. 1.88 + // TODO(nsylvain): If the process is still alive at the end, we should kill 1.89 + // it. http://b/893891 1.90 + // For now, this wait is there only to do a best effort to prevent some leaks 1.91 + // from showing up in purify. 1.92 + if (sandbox_process_info_.IsValid()) { 1.93 + ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50); 1.94 + if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(), 1.95 + &exit_code) || (STILL_ACTIVE == exit_code)) { 1.96 + // It is an error to destroy this object while the target process is still 1.97 + // alive because we need to destroy the IPC subsystem and cannot risk to 1.98 + // have an IPC reach us after this point. 1.99 + if (shared_section_.IsValid()) 1.100 + shared_section_.Take(); 1.101 + SharedMemIPCServer* server = ipc_server_.release(); 1.102 + sandbox_process_info_.TakeProcessHandle(); 1.103 + return; 1.104 + } 1.105 + } 1.106 + 1.107 + // ipc_server_ references our process handle, so make sure the former is shut 1.108 + // down before the latter is closed (by ScopedProcessInformation). 1.109 + ipc_server_.reset(); 1.110 +} 1.111 + 1.112 +// Creates the target (child) process suspended and assigns it to the job 1.113 +// object. 1.114 +DWORD TargetProcess::Create(const wchar_t* exe_path, 1.115 + const wchar_t* command_line, 1.116 + bool inherit_handles, 1.117 + const base::win::StartupInformation& startup_info, 1.118 + base::win::ScopedProcessInformation* target_info) { 1.119 + exe_name_.reset(_wcsdup(exe_path)); 1.120 + 1.121 + // the command line needs to be writable by CreateProcess(). 1.122 + scoped_ptr_malloc<wchar_t> cmd_line(_wcsdup(command_line)); 1.123 + 1.124 + // Start the target process suspended. 1.125 + DWORD flags = 1.126 + CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; 1.127 + 1.128 + if (startup_info.has_extended_startup_info()) 1.129 + flags |= EXTENDED_STARTUPINFO_PRESENT; 1.130 + 1.131 + if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) { 1.132 + // Windows 8 implements nested jobs, but for older systems we need to 1.133 + // break out of any job we're in to enforce our restrictions. 1.134 + flags |= CREATE_BREAKAWAY_FROM_JOB; 1.135 + } 1.136 + 1.137 + base::win::ScopedProcessInformation process_info; 1.138 + 1.139 + if (!::CreateProcessAsUserW(lockdown_token_, 1.140 + exe_path, 1.141 + cmd_line.get(), 1.142 + NULL, // No security attribute. 1.143 + NULL, // No thread attribute. 1.144 + inherit_handles, 1.145 + flags, 1.146 + NULL, // Use the environment of the caller. 1.147 + NULL, // Use current directory of the caller. 1.148 + startup_info.startup_info(), 1.149 + process_info.Receive())) { 1.150 + return ::GetLastError(); 1.151 + } 1.152 + lockdown_token_.Close(); 1.153 + 1.154 + DWORD win_result = ERROR_SUCCESS; 1.155 + 1.156 + if (job_) { 1.157 + // Assign the suspended target to the windows job object. 1.158 + if (!::AssignProcessToJobObject(job_, process_info.process_handle())) { 1.159 + win_result = ::GetLastError(); 1.160 + ::TerminateProcess(process_info.process_handle(), 0); 1.161 + return win_result; 1.162 + } 1.163 + } 1.164 + 1.165 + if (initial_token_.IsValid()) { 1.166 + // Change the token of the main thread of the new process for the 1.167 + // impersonation token with more rights. This allows the target to start; 1.168 + // otherwise it will crash too early for us to help. 1.169 + HANDLE temp_thread = process_info.thread_handle(); 1.170 + if (!::SetThreadToken(&temp_thread, initial_token_)) { 1.171 + win_result = ::GetLastError(); 1.172 + // It might be a security breach if we let the target run outside the job 1.173 + // so kill it before it causes damage. 1.174 + ::TerminateProcess(process_info.process_handle(), 0); 1.175 + return win_result; 1.176 + } 1.177 + initial_token_.Close(); 1.178 + } 1.179 + 1.180 + CONTEXT context; 1.181 + context.ContextFlags = CONTEXT_ALL; 1.182 + if (!::GetThreadContext(process_info.thread_handle(), &context)) { 1.183 + win_result = ::GetLastError(); 1.184 + ::TerminateProcess(process_info.process_handle(), 0); 1.185 + return win_result; 1.186 + } 1.187 + 1.188 +#if defined(_WIN64) 1.189 + void* entry_point = reinterpret_cast<void*>(context.Rcx); 1.190 +#else 1.191 +#pragma warning(push) 1.192 +#pragma warning(disable: 4312) 1.193 + // This cast generates a warning because it is 32 bit specific. 1.194 + void* entry_point = reinterpret_cast<void*>(context.Eax); 1.195 +#pragma warning(pop) 1.196 +#endif // _WIN64 1.197 + 1.198 + if (!target_info->DuplicateFrom(process_info)) { 1.199 + win_result = ::GetLastError(); // This may or may not be correct. 1.200 + ::TerminateProcess(process_info.process_handle(), 0); 1.201 + return win_result; 1.202 + } 1.203 + 1.204 + base_address_ = GetBaseAddress(exe_path, entry_point); 1.205 + sandbox_process_info_.Set(process_info.Take()); 1.206 + return win_result; 1.207 +} 1.208 + 1.209 +ResultCode TargetProcess::TransferVariable(const char* name, void* address, 1.210 + size_t size) { 1.211 + if (!sandbox_process_info_.IsValid()) 1.212 + return SBOX_ERROR_UNEXPECTED_CALL; 1.213 + 1.214 + void* child_var = address; 1.215 + 1.216 +#if SANDBOX_EXPORTS 1.217 + HMODULE module = ::LoadLibrary(exe_name_.get()); 1.218 + if (NULL == module) 1.219 + return SBOX_ERROR_GENERIC; 1.220 + 1.221 + child_var = ::GetProcAddress(module, name); 1.222 + ::FreeLibrary(module); 1.223 + 1.224 + if (NULL == child_var) 1.225 + return SBOX_ERROR_GENERIC; 1.226 + 1.227 + size_t offset = reinterpret_cast<char*>(child_var) - 1.228 + reinterpret_cast<char*>(module); 1.229 + child_var = reinterpret_cast<char*>(MainModule()) + offset; 1.230 +#else 1.231 + UNREFERENCED_PARAMETER(name); 1.232 +#endif 1.233 + 1.234 + SIZE_T written; 1.235 + if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), 1.236 + child_var, address, size, &written)) 1.237 + return SBOX_ERROR_GENERIC; 1.238 + 1.239 + if (written != size) 1.240 + return SBOX_ERROR_GENERIC; 1.241 + 1.242 + return SBOX_ALL_OK; 1.243 +} 1.244 + 1.245 +// Construct the IPC server and the IPC dispatcher. When the target does 1.246 +// an IPC it will eventually call the dispatcher. 1.247 +DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy, 1.248 + uint32 shared_IPC_size, uint32 shared_policy_size) { 1.249 + // We need to map the shared memory on the target. This is necessary for 1.250 + // any IPC that needs to take place, even if the target has not yet hit 1.251 + // the main( ) function or even has initialized the CRT. So here we set 1.252 + // the handle to the shared section. The target on the first IPC must do 1.253 + // the rest, which boils down to calling MapViewofFile() 1.254 + 1.255 + // We use this single memory pool for IPC and for policy. 1.256 + DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size + 1.257 + shared_policy_size); 1.258 + shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, 1.259 + PAGE_READWRITE | SEC_COMMIT, 1.260 + 0, shared_mem_size, NULL)); 1.261 + if (!shared_section_.IsValid()) { 1.262 + return ::GetLastError(); 1.263 + } 1.264 + 1.265 + DWORD access = FILE_MAP_READ | FILE_MAP_WRITE; 1.266 + HANDLE target_shared_section; 1.267 + if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_, 1.268 + sandbox_process_info_.process_handle(), 1.269 + &target_shared_section, access, FALSE, 0)) { 1.270 + return ::GetLastError(); 1.271 + } 1.272 + 1.273 + void* shared_memory = ::MapViewOfFile(shared_section_, 1.274 + FILE_MAP_WRITE|FILE_MAP_READ, 1.275 + 0, 0, 0); 1.276 + if (NULL == shared_memory) { 1.277 + return ::GetLastError(); 1.278 + } 1.279 + 1.280 + CopyPolicyToTarget(policy, shared_policy_size, 1.281 + reinterpret_cast<char*>(shared_memory) + shared_IPC_size); 1.282 + 1.283 + ResultCode ret; 1.284 + // Set the global variables in the target. These are not used on the broker. 1.285 + g_shared_section = target_shared_section; 1.286 + ret = TransferVariable("g_shared_section", &g_shared_section, 1.287 + sizeof(g_shared_section)); 1.288 + g_shared_section = NULL; 1.289 + if (SBOX_ALL_OK != ret) { 1.290 + return (SBOX_ERROR_GENERIC == ret)? 1.291 + ::GetLastError() : ERROR_INVALID_FUNCTION; 1.292 + } 1.293 + g_shared_IPC_size = shared_IPC_size; 1.294 + ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size, 1.295 + sizeof(g_shared_IPC_size)); 1.296 + g_shared_IPC_size = 0; 1.297 + if (SBOX_ALL_OK != ret) { 1.298 + return (SBOX_ERROR_GENERIC == ret) ? 1.299 + ::GetLastError() : ERROR_INVALID_FUNCTION; 1.300 + } 1.301 + g_shared_policy_size = shared_policy_size; 1.302 + ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size, 1.303 + sizeof(g_shared_policy_size)); 1.304 + g_shared_policy_size = 0; 1.305 + if (SBOX_ALL_OK != ret) { 1.306 + return (SBOX_ERROR_GENERIC == ret) ? 1.307 + ::GetLastError() : ERROR_INVALID_FUNCTION; 1.308 + } 1.309 + 1.310 + ipc_server_.reset( 1.311 + new SharedMemIPCServer(sandbox_process_info_.process_handle(), 1.312 + sandbox_process_info_.process_id(), 1.313 + job_, thread_pool_, ipc_dispatcher)); 1.314 + 1.315 + if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize)) 1.316 + return ERROR_NOT_ENOUGH_MEMORY; 1.317 + 1.318 + // After this point we cannot use this handle anymore. 1.319 + ::CloseHandle(sandbox_process_info_.TakeThreadHandle()); 1.320 + 1.321 + return ERROR_SUCCESS; 1.322 +} 1.323 + 1.324 +void TargetProcess::Terminate() { 1.325 + if (!sandbox_process_info_.IsValid()) 1.326 + return; 1.327 + 1.328 + ::TerminateProcess(sandbox_process_info_.process_handle(), 0); 1.329 +} 1.330 + 1.331 +TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { 1.332 + TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL); 1.333 + PROCESS_INFORMATION process_info = {}; 1.334 + process_info.hProcess = process; 1.335 + target->sandbox_process_info_.Set(process_info); 1.336 + target->base_address_ = base_address; 1.337 + return target; 1.338 +} 1.339 + 1.340 +} // namespace sandbox