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 |