| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sandbox/win/src/target_process.h" | 5 #include "sandbox/win/src/target_process.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <memory> | 10 #include <memory> |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 } | 112 } |
| 113 } | 113 } |
| 114 | 114 |
| 115 // ipc_server_ references our process handle, so make sure the former is shut | 115 // ipc_server_ references our process handle, so make sure the former is shut |
| 116 // down before the latter is closed (by ScopedProcessInformation). | 116 // down before the latter is closed (by ScopedProcessInformation). |
| 117 ipc_server_.reset(); | 117 ipc_server_.reset(); |
| 118 } | 118 } |
| 119 | 119 |
| 120 // Creates the target (child) process suspended and assigns it to the job | 120 // Creates the target (child) process suspended and assigns it to the job |
| 121 // object. | 121 // object. |
| 122 DWORD TargetProcess::Create(const wchar_t* exe_path, | 122 ResultCode TargetProcess::Create( |
| 123 const wchar_t* command_line, | 123 const wchar_t* exe_path, |
| 124 bool inherit_handles, | 124 const wchar_t* command_line, |
| 125 const base::win::StartupInformation& startup_info, | 125 bool inherit_handles, |
| 126 base::win::ScopedProcessInformation* target_info) { | 126 const base::win::StartupInformation& startup_info, |
| 127 base::win::ScopedProcessInformation* target_info, |
| 128 DWORD* win_error) { |
| 127 if (lowbox_token_.IsValid() && | 129 if (lowbox_token_.IsValid() && |
| 128 base::win::GetVersion() < base::win::VERSION_WIN8) { | 130 base::win::GetVersion() < base::win::VERSION_WIN8) { |
| 129 // We don't allow lowbox_token below Windows 8. | 131 // We don't allow lowbox_token below Windows 8. |
| 130 return ERROR_INVALID_PARAMETER; | 132 return SBOX_ERROR_BAD_PARAMS; |
| 131 } | 133 } |
| 132 | 134 |
| 133 exe_name_.reset(_wcsdup(exe_path)); | 135 exe_name_.reset(_wcsdup(exe_path)); |
| 134 | 136 |
| 135 // the command line needs to be writable by CreateProcess(). | 137 // the command line needs to be writable by CreateProcess(). |
| 136 std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line)); | 138 std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line)); |
| 137 | 139 |
| 138 // Start the target process suspended. | 140 // Start the target process suspended. |
| 139 DWORD flags = | 141 DWORD flags = |
| 140 CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; | 142 CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; |
| 141 | 143 |
| 142 if (startup_info.has_extended_startup_info()) | 144 if (startup_info.has_extended_startup_info()) |
| 143 flags |= EXTENDED_STARTUPINFO_PRESENT; | 145 flags |= EXTENDED_STARTUPINFO_PRESENT; |
| 144 | 146 |
| 145 if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) { | 147 if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) { |
| 146 // Windows 8 implements nested jobs, but for older systems we need to | 148 // Windows 8 implements nested jobs, but for older systems we need to |
| 147 // break out of any job we're in to enforce our restrictions. | 149 // break out of any job we're in to enforce our restrictions. |
| 148 flags |= CREATE_BREAKAWAY_FROM_JOB; | 150 flags |= CREATE_BREAKAWAY_FROM_JOB; |
| 149 } | 151 } |
| 150 | 152 |
| 151 PROCESS_INFORMATION temp_process_info = {}; | 153 PROCESS_INFORMATION temp_process_info = {}; |
| 152 if (!::CreateProcessAsUserW(lockdown_token_.Get(), exe_path, cmd_line.get(), | 154 if (!::CreateProcessAsUserW(lockdown_token_.Get(), exe_path, cmd_line.get(), |
| 153 NULL, // No security attribute. | 155 NULL, // No security attribute. |
| 154 NULL, // No thread attribute. | 156 NULL, // No thread attribute. |
| 155 inherit_handles, flags, | 157 inherit_handles, flags, |
| 156 NULL, // Use the environment of the caller. | 158 NULL, // Use the environment of the caller. |
| 157 NULL, // Use current directory of the caller. | 159 NULL, // Use current directory of the caller. |
| 158 startup_info.startup_info(), | 160 startup_info.startup_info(), |
| 159 &temp_process_info)) { | 161 &temp_process_info)) { |
| 160 return ::GetLastError(); | 162 *win_error = ::GetLastError(); |
| 163 return SBOX_ERROR_CREATE_PROCESS; |
| 161 } | 164 } |
| 162 base::win::ScopedProcessInformation process_info(temp_process_info); | 165 base::win::ScopedProcessInformation process_info(temp_process_info); |
| 163 | 166 |
| 164 DWORD win_result = ERROR_SUCCESS; | |
| 165 | |
| 166 if (job_) { | 167 if (job_) { |
| 167 // Assign the suspended target to the windows job object. | 168 // Assign the suspended target to the windows job object. |
| 168 if (!::AssignProcessToJobObject(job_, process_info.process_handle())) { | 169 if (!::AssignProcessToJobObject(job_, process_info.process_handle())) { |
| 169 win_result = ::GetLastError(); | 170 *win_error = ::GetLastError(); |
| 170 ::TerminateProcess(process_info.process_handle(), 0); | 171 ::TerminateProcess(process_info.process_handle(), 0); |
| 171 return win_result; | 172 return SBOX_ERROR_ASSIGN_PROCESS_TO_JOB_OBJECT; |
| 172 } | 173 } |
| 173 } | 174 } |
| 174 | 175 |
| 175 if (initial_token_.IsValid()) { | 176 if (initial_token_.IsValid()) { |
| 176 // Change the token of the main thread of the new process for the | 177 // Change the token of the main thread of the new process for the |
| 177 // impersonation token with more rights. This allows the target to start; | 178 // impersonation token with more rights. This allows the target to start; |
| 178 // otherwise it will crash too early for us to help. | 179 // otherwise it will crash too early for us to help. |
| 179 HANDLE temp_thread = process_info.thread_handle(); | 180 HANDLE temp_thread = process_info.thread_handle(); |
| 180 if (!::SetThreadToken(&temp_thread, initial_token_.Get())) { | 181 if (!::SetThreadToken(&temp_thread, initial_token_.Get())) { |
| 181 win_result = ::GetLastError(); | 182 *win_error = ::GetLastError(); |
| 182 // It might be a security breach if we let the target run outside the job | 183 // It might be a security breach if we let the target run outside the job |
| 183 // so kill it before it causes damage. | 184 // so kill it before it causes damage. |
| 184 ::TerminateProcess(process_info.process_handle(), 0); | 185 ::TerminateProcess(process_info.process_handle(), 0); |
| 185 return win_result; | 186 return SBOX_ERROR_SET_THREAD_TOKEN; |
| 186 } | 187 } |
| 187 initial_token_.Close(); | 188 initial_token_.Close(); |
| 188 } | 189 } |
| 189 | 190 |
| 190 CONTEXT context; | 191 CONTEXT context; |
| 191 context.ContextFlags = CONTEXT_ALL; | 192 context.ContextFlags = CONTEXT_ALL; |
| 192 if (!::GetThreadContext(process_info.thread_handle(), &context)) { | 193 if (!::GetThreadContext(process_info.thread_handle(), &context)) { |
| 193 win_result = ::GetLastError(); | 194 *win_error = ::GetLastError(); |
| 194 ::TerminateProcess(process_info.process_handle(), 0); | 195 ::TerminateProcess(process_info.process_handle(), 0); |
| 195 return win_result; | 196 return SBOX_ERROR_GET_THREAD_CONTEXT; |
| 196 } | 197 } |
| 197 | 198 |
| 198 #if defined(_WIN64) | 199 #if defined(_WIN64) |
| 199 void* entry_point = reinterpret_cast<void*>(context.Rcx); | 200 void* entry_point = reinterpret_cast<void*>(context.Rcx); |
| 200 #else | 201 #else |
| 201 #pragma warning(push) | 202 #pragma warning(push) |
| 202 #pragma warning(disable: 4312) | 203 #pragma warning(disable: 4312) |
| 203 // This cast generates a warning because it is 32 bit specific. | 204 // This cast generates a warning because it is 32 bit specific. |
| 204 void* entry_point = reinterpret_cast<void*>(context.Eax); | 205 void* entry_point = reinterpret_cast<void*>(context.Eax); |
| 205 #pragma warning(pop) | 206 #pragma warning(pop) |
| 206 #endif // _WIN64 | 207 #endif // _WIN64 |
| 207 | 208 |
| 208 if (!target_info->DuplicateFrom(process_info)) { | 209 if (!target_info->DuplicateFrom(process_info)) { |
| 209 win_result = ::GetLastError(); // This may or may not be correct. | 210 *win_error = ::GetLastError(); // This may or may not be correct. |
| 210 ::TerminateProcess(process_info.process_handle(), 0); | 211 ::TerminateProcess(process_info.process_handle(), 0); |
| 211 return win_result; | 212 return SBOX_ERROR_DUPLICATE_TARGET_INFO; |
| 212 } | 213 } |
| 213 | 214 |
| 214 if (lowbox_token_.IsValid()) { | 215 if (lowbox_token_.IsValid()) { |
| 215 PROCESS_ACCESS_TOKEN process_access_token; | 216 PROCESS_ACCESS_TOKEN process_access_token; |
| 216 process_access_token.thread = process_info.thread_handle(); | 217 process_access_token.thread = process_info.thread_handle(); |
| 217 process_access_token.token = lowbox_token_.Get(); | 218 process_access_token.token = lowbox_token_.Get(); |
| 218 | 219 |
| 219 NtSetInformationProcess SetInformationProcess = NULL; | 220 NtSetInformationProcess SetInformationProcess = NULL; |
| 220 ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess); | 221 ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess); |
| 221 | 222 |
| 222 NTSTATUS status = SetInformationProcess( | 223 NTSTATUS status = SetInformationProcess( |
| 223 process_info.process_handle(), | 224 process_info.process_handle(), |
| 224 static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken), | 225 static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken), |
| 225 &process_access_token, sizeof(process_access_token)); | 226 &process_access_token, sizeof(process_access_token)); |
| 226 if (!NT_SUCCESS(status)) { | 227 if (!NT_SUCCESS(status)) { |
| 227 win_result = ERROR_INVALID_TOKEN; | 228 *win_error = ERROR_INVALID_TOKEN; |
| 228 ::TerminateProcess(process_info.process_handle(), 0); // exit code | 229 ::TerminateProcess(process_info.process_handle(), 0); // exit code |
| 229 return win_result; | 230 return SBOX_ERROR_SET_LOW_BOX_TOKEN; |
| 230 } | 231 } |
| 231 } | 232 } |
| 232 | 233 |
| 233 base_address_ = GetBaseAddress(exe_path, entry_point); | 234 base_address_ = GetBaseAddress(exe_path, entry_point); |
| 234 sandbox_process_info_.Set(process_info.Take()); | 235 sandbox_process_info_.Set(process_info.Take()); |
| 235 return win_result; | 236 return SBOX_ALL_OK; |
| 236 } | 237 } |
| 237 | 238 |
| 238 ResultCode TargetProcess::TransferVariable(const char* name, void* address, | 239 ResultCode TargetProcess::TransferVariable(const char* name, void* address, |
| 239 size_t size) { | 240 size_t size) { |
| 240 if (!sandbox_process_info_.IsValid()) | 241 if (!sandbox_process_info_.IsValid()) |
| 241 return SBOX_ERROR_UNEXPECTED_CALL; | 242 return SBOX_ERROR_UNEXPECTED_CALL; |
| 242 | 243 |
| 243 void* child_var = address; | 244 void* child_var = address; |
| 244 | 245 |
| 245 #if SANDBOX_EXPORTS | 246 #if SANDBOX_EXPORTS |
| (...skipping 18 matching lines...) Expand all Loading... |
| 264 return SBOX_ERROR_GENERIC; | 265 return SBOX_ERROR_GENERIC; |
| 265 | 266 |
| 266 if (written != size) | 267 if (written != size) |
| 267 return SBOX_ERROR_GENERIC; | 268 return SBOX_ERROR_GENERIC; |
| 268 | 269 |
| 269 return SBOX_ALL_OK; | 270 return SBOX_ALL_OK; |
| 270 } | 271 } |
| 271 | 272 |
| 272 // Construct the IPC server and the IPC dispatcher. When the target does | 273 // Construct the IPC server and the IPC dispatcher. When the target does |
| 273 // an IPC it will eventually call the dispatcher. | 274 // an IPC it will eventually call the dispatcher. |
| 274 DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, | 275 ResultCode TargetProcess::Init(Dispatcher* ipc_dispatcher, |
| 275 void* policy, | 276 void* policy, |
| 276 uint32_t shared_IPC_size, | 277 uint32_t shared_IPC_size, |
| 277 uint32_t shared_policy_size) { | 278 uint32_t shared_policy_size, |
| 279 DWORD* win_error) { |
| 278 // We need to map the shared memory on the target. This is necessary for | 280 // We need to map the shared memory on the target. This is necessary for |
| 279 // any IPC that needs to take place, even if the target has not yet hit | 281 // any IPC that needs to take place, even if the target has not yet hit |
| 280 // the main( ) function or even has initialized the CRT. So here we set | 282 // the main( ) function or even has initialized the CRT. So here we set |
| 281 // the handle to the shared section. The target on the first IPC must do | 283 // the handle to the shared section. The target on the first IPC must do |
| 282 // the rest, which boils down to calling MapViewofFile() | 284 // the rest, which boils down to calling MapViewofFile() |
| 283 | 285 |
| 284 // We use this single memory pool for IPC and for policy. | 286 // We use this single memory pool for IPC and for policy. |
| 285 DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size + | 287 DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size + |
| 286 shared_policy_size); | 288 shared_policy_size); |
| 287 shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, | 289 shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, |
| 288 PAGE_READWRITE | SEC_COMMIT, | 290 PAGE_READWRITE | SEC_COMMIT, |
| 289 0, shared_mem_size, NULL)); | 291 0, shared_mem_size, NULL)); |
| 290 if (!shared_section_.IsValid()) { | 292 if (!shared_section_.IsValid()) { |
| 291 return ::GetLastError(); | 293 *win_error = ::GetLastError(); |
| 294 return SBOX_ERROR_CREATE_FILE_MAPPING; |
| 292 } | 295 } |
| 293 | 296 |
| 294 DWORD access = FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY; | 297 DWORD access = FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY; |
| 295 HANDLE target_shared_section; | 298 HANDLE target_shared_section; |
| 296 if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_.Get(), | 299 if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_.Get(), |
| 297 sandbox_process_info_.process_handle(), | 300 sandbox_process_info_.process_handle(), |
| 298 &target_shared_section, access, FALSE, 0)) { | 301 &target_shared_section, access, FALSE, 0)) { |
| 299 return ::GetLastError(); | 302 *win_error = ::GetLastError(); |
| 303 return SBOX_ERROR_DUPLICATE_SHARED_SECTION; |
| 300 } | 304 } |
| 301 | 305 |
| 302 void* shared_memory = ::MapViewOfFile(shared_section_.Get(), | 306 void* shared_memory = ::MapViewOfFile(shared_section_.Get(), |
| 303 FILE_MAP_WRITE|FILE_MAP_READ, | 307 FILE_MAP_WRITE|FILE_MAP_READ, |
| 304 0, 0, 0); | 308 0, 0, 0); |
| 305 if (NULL == shared_memory) { | 309 if (NULL == shared_memory) { |
| 306 return ::GetLastError(); | 310 *win_error = ::GetLastError(); |
| 311 return SBOX_ERROR_MAP_VIEW_OF_SHARED_SECTION; |
| 307 } | 312 } |
| 308 | 313 |
| 309 CopyPolicyToTarget(policy, shared_policy_size, | 314 CopyPolicyToTarget(policy, shared_policy_size, |
| 310 reinterpret_cast<char*>(shared_memory) + shared_IPC_size); | 315 reinterpret_cast<char*>(shared_memory) + shared_IPC_size); |
| 311 | 316 |
| 312 ResultCode ret; | 317 ResultCode ret; |
| 313 // Set the global variables in the target. These are not used on the broker. | 318 // Set the global variables in the target. These are not used on the broker. |
| 314 g_shared_section = target_shared_section; | 319 g_shared_section = target_shared_section; |
| 315 ret = TransferVariable("g_shared_section", &g_shared_section, | 320 ret = TransferVariable("g_shared_section", &g_shared_section, |
| 316 sizeof(g_shared_section)); | 321 sizeof(g_shared_section)); |
| 317 g_shared_section = NULL; | 322 g_shared_section = NULL; |
| 318 if (SBOX_ALL_OK != ret) { | 323 if (SBOX_ALL_OK != ret) { |
| 319 return (SBOX_ERROR_GENERIC == ret)? | 324 *win_error = ::GetLastError(); |
| 320 ::GetLastError() : ERROR_INVALID_FUNCTION; | 325 return ret; |
| 321 } | 326 } |
| 322 g_shared_IPC_size = shared_IPC_size; | 327 g_shared_IPC_size = shared_IPC_size; |
| 323 ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size, | 328 ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size, |
| 324 sizeof(g_shared_IPC_size)); | 329 sizeof(g_shared_IPC_size)); |
| 325 g_shared_IPC_size = 0; | 330 g_shared_IPC_size = 0; |
| 326 if (SBOX_ALL_OK != ret) { | 331 if (SBOX_ALL_OK != ret) { |
| 327 return (SBOX_ERROR_GENERIC == ret) ? | 332 *win_error = ::GetLastError(); |
| 328 ::GetLastError() : ERROR_INVALID_FUNCTION; | 333 return ret; |
| 329 } | 334 } |
| 330 g_shared_policy_size = shared_policy_size; | 335 g_shared_policy_size = shared_policy_size; |
| 331 ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size, | 336 ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size, |
| 332 sizeof(g_shared_policy_size)); | 337 sizeof(g_shared_policy_size)); |
| 333 g_shared_policy_size = 0; | 338 g_shared_policy_size = 0; |
| 334 if (SBOX_ALL_OK != ret) { | 339 if (SBOX_ALL_OK != ret) { |
| 335 return (SBOX_ERROR_GENERIC == ret) ? | 340 *win_error = ::GetLastError(); |
| 336 ::GetLastError() : ERROR_INVALID_FUNCTION; | 341 return ret; |
| 337 } | 342 } |
| 338 | 343 |
| 339 ipc_server_.reset( | 344 ipc_server_.reset( |
| 340 new SharedMemIPCServer(sandbox_process_info_.process_handle(), | 345 new SharedMemIPCServer(sandbox_process_info_.process_handle(), |
| 341 sandbox_process_info_.process_id(), | 346 sandbox_process_info_.process_id(), |
| 342 thread_pool_, ipc_dispatcher)); | 347 thread_pool_, ipc_dispatcher)); |
| 343 | 348 |
| 344 if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize)) | 349 if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize)) |
| 345 return ERROR_NOT_ENOUGH_MEMORY; | 350 return SBOX_ERROR_NO_SPACE; |
| 346 | 351 |
| 347 // After this point we cannot use this handle anymore. | 352 // After this point we cannot use this handle anymore. |
| 348 ::CloseHandle(sandbox_process_info_.TakeThreadHandle()); | 353 ::CloseHandle(sandbox_process_info_.TakeThreadHandle()); |
| 349 | 354 |
| 350 return ERROR_SUCCESS; | 355 return SBOX_ALL_OK; |
| 351 } | 356 } |
| 352 | 357 |
| 353 void TargetProcess::Terminate() { | 358 void TargetProcess::Terminate() { |
| 354 if (!sandbox_process_info_.IsValid()) | 359 if (!sandbox_process_info_.IsValid()) |
| 355 return; | 360 return; |
| 356 | 361 |
| 357 ::TerminateProcess(sandbox_process_info_.process_handle(), 0); | 362 ::TerminateProcess(sandbox_process_info_.process_handle(), 0); |
| 358 } | 363 } |
| 359 | 364 |
| 360 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { | 365 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { |
| 361 TargetProcess* target = | 366 TargetProcess* target = |
| 362 new TargetProcess(base::win::ScopedHandle(), base::win::ScopedHandle(), | 367 new TargetProcess(base::win::ScopedHandle(), base::win::ScopedHandle(), |
| 363 base::win::ScopedHandle(), NULL, NULL); | 368 base::win::ScopedHandle(), NULL, NULL); |
| 364 PROCESS_INFORMATION process_info = {}; | 369 PROCESS_INFORMATION process_info = {}; |
| 365 process_info.hProcess = process; | 370 process_info.hProcess = process; |
| 366 target->sandbox_process_info_.Set(process_info); | 371 target->sandbox_process_info_.Set(process_info); |
| 367 target->base_address_ = base_address; | 372 target->base_address_ = base_address; |
| 368 return target; | 373 return target; |
| 369 } | 374 } |
| 370 | 375 |
| 371 } // namespace sandbox | 376 } // namespace sandbox |
| OLD | NEW |