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