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"
17 #include "sandbox/win/src/win_utils.h"
21 void CopyPolicyToTarget(const void* source
, size_t size
, void* dest
) {
24 memcpy(dest
, source
, size
);
25 sandbox::PolicyGlobal
* policy
=
26 reinterpret_cast<sandbox::PolicyGlobal
*>(dest
);
28 size_t offset
= reinterpret_cast<size_t>(source
);
30 for (size_t i
= 0; i
< sandbox::kMaxServiceCount
; i
++) {
31 size_t buffer
= reinterpret_cast<size_t>(policy
->entry
[i
]);
34 policy
->entry
[i
] = reinterpret_cast<sandbox::PolicyBuffer
*>(buffer
);
43 SANDBOX_INTERCEPT HANDLE g_shared_section
;
44 SANDBOX_INTERCEPT
size_t g_shared_IPC_size
;
45 SANDBOX_INTERCEPT
size_t g_shared_policy_size
;
47 // Returns the address of the main exe module in memory taking in account
48 // address space layout randomization.
49 void* GetBaseAddress(const wchar_t* exe_name
, void* entry_point
) {
50 HMODULE exe
= ::LoadLibrary(exe_name
);
54 base::win::PEImage
pe(exe
);
55 if (!pe
.VerifyMagic()) {
59 PIMAGE_NT_HEADERS nt_header
= pe
.GetNTHeaders();
60 char* base
= reinterpret_cast<char*>(entry_point
) -
61 nt_header
->OptionalHeader
.AddressOfEntryPoint
;
68 TargetProcess::TargetProcess(HANDLE initial_token
, HANDLE lockdown_token
,
69 HANDLE job
, ThreadProvider
* thread_pool
)
70 // This object owns everything initialized here except thread_pool and
71 // the job_ handle. The Job handle is closed by BrokerServices and results
72 // eventually in a call to our dtor.
73 : lockdown_token_(lockdown_token
),
74 initial_token_(initial_token
),
76 thread_pool_(thread_pool
),
80 TargetProcess::~TargetProcess() {
82 // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
83 // will take effect only when the context changes. As far as the testing went,
84 // this wait was enough to switch context and kill the processes in the job.
85 // If this process is already dead, the function will return without waiting.
86 // TODO(nsylvain): If the process is still alive at the end, we should kill
87 // it. http://b/893891
88 // For now, this wait is there only to do a best effort to prevent some leaks
89 // from showing up in purify.
90 if (sandbox_process_info_
.IsValid()) {
91 ::WaitForSingleObject(sandbox_process_info_
.process_handle(), 50);
92 // At this point, the target process should have been killed. Check.
93 if (!::GetExitCodeProcess(sandbox_process_info_
.process_handle(),
94 &exit_code
) || (STILL_ACTIVE
== exit_code
)) {
95 // Something went wrong. We don't know if the target is in a state where
96 // it can manage to do another IPC call. If it can, and we've destroyed
97 // the |ipc_server_|, it will crash the broker. So we intentionally leak
99 if (shared_section_
.IsValid())
100 shared_section_
.Take();
101 ipc_server_
.release();
102 sandbox_process_info_
.TakeProcessHandle();
107 // ipc_server_ references our process handle, so make sure the former is shut
108 // down before the latter is closed (by ScopedProcessInformation).
112 // Creates the target (child) process suspended and assigns it to the job
114 DWORD
TargetProcess::Create(const wchar_t* exe_path
,
115 const wchar_t* command_line
,
116 bool inherit_handles
,
117 bool set_lockdown_token_after_create
,
118 const base::win::StartupInformation
& startup_info
,
119 base::win::ScopedProcessInformation
* target_info
) {
120 if (set_lockdown_token_after_create
&&
121 base::win::GetVersion() < base::win::VERSION_WIN8
) {
122 // We don't allow set_lockdown_token_after_create below Windows 8.
123 return ERROR_INVALID_PARAMETER
;
126 exe_name_
.reset(_wcsdup(exe_path
));
128 // the command line needs to be writable by CreateProcess().
129 scoped_ptr
<wchar_t, base::FreeDeleter
> cmd_line(_wcsdup(command_line
));
131 // Start the target process suspended.
133 CREATE_SUSPENDED
| CREATE_UNICODE_ENVIRONMENT
| DETACHED_PROCESS
;
135 if (startup_info
.has_extended_startup_info())
136 flags
|= EXTENDED_STARTUPINFO_PRESENT
;
138 if (job_
&& base::win::GetVersion() < base::win::VERSION_WIN8
) {
139 // Windows 8 implements nested jobs, but for older systems we need to
140 // break out of any job we're in to enforce our restrictions.
141 flags
|= CREATE_BREAKAWAY_FROM_JOB
;
144 base::win::ScopedHandle
scoped_lockdown_token(lockdown_token_
.Take());
145 PROCESS_INFORMATION temp_process_info
= {};
146 if (set_lockdown_token_after_create
) {
147 // First create process with a default token and then replace it later,
148 // after setting primary thread token. This is required for setting
149 // an AppContainer token along with an impersonation token.
150 if (!::CreateProcess(exe_path
,
152 NULL
, // No security attribute.
153 NULL
, // No thread attribute.
156 NULL
, // Use the environment of the caller.
157 NULL
, // Use current directory of the caller.
158 startup_info
.startup_info(),
159 &temp_process_info
)) {
160 return ::GetLastError();
163 if (!::CreateProcessAsUserW(scoped_lockdown_token
.Get(),
166 NULL
, // No security attribute.
167 NULL
, // No thread attribute.
170 NULL
, // Use the environment of the caller.
171 NULL
, // Use current directory of the caller.
172 startup_info
.startup_info(),
173 &temp_process_info
)) {
174 return ::GetLastError();
177 base::win::ScopedProcessInformation
process_info(temp_process_info
);
179 DWORD win_result
= ERROR_SUCCESS
;
182 // Assign the suspended target to the windows job object.
183 if (!::AssignProcessToJobObject(job_
, process_info
.process_handle())) {
184 win_result
= ::GetLastError();
185 ::TerminateProcess(process_info
.process_handle(), 0);
190 if (initial_token_
.IsValid()) {
191 // Change the token of the main thread of the new process for the
192 // impersonation token with more rights. This allows the target to start;
193 // otherwise it will crash too early for us to help.
194 HANDLE temp_thread
= process_info
.thread_handle();
195 if (!::SetThreadToken(&temp_thread
, initial_token_
.Get())) {
196 win_result
= ::GetLastError();
197 // It might be a security breach if we let the target run outside the job
198 // so kill it before it causes damage.
199 ::TerminateProcess(process_info
.process_handle(), 0);
202 initial_token_
.Close();
205 if (set_lockdown_token_after_create
) {
206 PROCESS_ACCESS_TOKEN process_access_token
;
207 process_access_token
.thread
= process_info
.thread_handle();
208 process_access_token
.token
= scoped_lockdown_token
.Get();
210 NtSetInformationProcess SetInformationProcess
= NULL
;
211 ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess
);
213 NTSTATUS status
= SetInformationProcess(
214 process_info
.process_handle(),
215 static_cast<PROCESS_INFORMATION_CLASS
>(NtProcessInformationAccessToken
),
216 &process_access_token
,
217 sizeof(process_access_token
));
218 if (!NT_SUCCESS(status
)) {
219 win_result
= ::GetLastError();
220 ::TerminateProcess(process_info
.process_handle(), 0); // exit code
226 context
.ContextFlags
= CONTEXT_ALL
;
227 if (!::GetThreadContext(process_info
.thread_handle(), &context
)) {
228 win_result
= ::GetLastError();
229 ::TerminateProcess(process_info
.process_handle(), 0);
234 void* entry_point
= reinterpret_cast<void*>(context
.Rcx
);
236 #pragma warning(push)
237 #pragma warning(disable: 4312)
238 // This cast generates a warning because it is 32 bit specific.
239 void* entry_point
= reinterpret_cast<void*>(context
.Eax
);
243 if (!target_info
->DuplicateFrom(process_info
)) {
244 win_result
= ::GetLastError(); // This may or may not be correct.
245 ::TerminateProcess(process_info
.process_handle(), 0);
249 base_address_
= GetBaseAddress(exe_path
, entry_point
);
250 sandbox_process_info_
.Set(process_info
.Take());
254 ResultCode
TargetProcess::TransferVariable(const char* name
, void* address
,
256 if (!sandbox_process_info_
.IsValid())
257 return SBOX_ERROR_UNEXPECTED_CALL
;
259 void* child_var
= address
;
262 HMODULE module
= ::LoadLibrary(exe_name_
.get());
264 return SBOX_ERROR_GENERIC
;
266 child_var
= ::GetProcAddress(module
, name
);
267 ::FreeLibrary(module
);
269 if (NULL
== child_var
)
270 return SBOX_ERROR_GENERIC
;
272 size_t offset
= reinterpret_cast<char*>(child_var
) -
273 reinterpret_cast<char*>(module
);
274 child_var
= reinterpret_cast<char*>(MainModule()) + offset
;
276 UNREFERENCED_PARAMETER(name
);
280 if (!::WriteProcessMemory(sandbox_process_info_
.process_handle(),
281 child_var
, address
, size
, &written
))
282 return SBOX_ERROR_GENERIC
;
285 return SBOX_ERROR_GENERIC
;
290 // Construct the IPC server and the IPC dispatcher. When the target does
291 // an IPC it will eventually call the dispatcher.
292 DWORD
TargetProcess::Init(Dispatcher
* ipc_dispatcher
, void* policy
,
293 uint32 shared_IPC_size
, uint32 shared_policy_size
) {
294 // We need to map the shared memory on the target. This is necessary for
295 // any IPC that needs to take place, even if the target has not yet hit
296 // the main( ) function or even has initialized the CRT. So here we set
297 // the handle to the shared section. The target on the first IPC must do
298 // the rest, which boils down to calling MapViewofFile()
300 // We use this single memory pool for IPC and for policy.
301 DWORD shared_mem_size
= static_cast<DWORD
>(shared_IPC_size
+
303 shared_section_
.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE
, NULL
,
304 PAGE_READWRITE
| SEC_COMMIT
,
305 0, shared_mem_size
, NULL
));
306 if (!shared_section_
.IsValid()) {
307 return ::GetLastError();
310 DWORD access
= FILE_MAP_READ
| FILE_MAP_WRITE
;
311 HANDLE target_shared_section
;
312 if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_
.Get(),
313 sandbox_process_info_
.process_handle(),
314 &target_shared_section
, access
, FALSE
, 0)) {
315 return ::GetLastError();
318 void* shared_memory
= ::MapViewOfFile(shared_section_
.Get(),
319 FILE_MAP_WRITE
|FILE_MAP_READ
,
321 if (NULL
== shared_memory
) {
322 return ::GetLastError();
325 CopyPolicyToTarget(policy
, shared_policy_size
,
326 reinterpret_cast<char*>(shared_memory
) + shared_IPC_size
);
329 // Set the global variables in the target. These are not used on the broker.
330 g_shared_section
= target_shared_section
;
331 ret
= TransferVariable("g_shared_section", &g_shared_section
,
332 sizeof(g_shared_section
));
333 g_shared_section
= NULL
;
334 if (SBOX_ALL_OK
!= ret
) {
335 return (SBOX_ERROR_GENERIC
== ret
)?
336 ::GetLastError() : ERROR_INVALID_FUNCTION
;
338 g_shared_IPC_size
= shared_IPC_size
;
339 ret
= TransferVariable("g_shared_IPC_size", &g_shared_IPC_size
,
340 sizeof(g_shared_IPC_size
));
341 g_shared_IPC_size
= 0;
342 if (SBOX_ALL_OK
!= ret
) {
343 return (SBOX_ERROR_GENERIC
== ret
) ?
344 ::GetLastError() : ERROR_INVALID_FUNCTION
;
346 g_shared_policy_size
= shared_policy_size
;
347 ret
= TransferVariable("g_shared_policy_size", &g_shared_policy_size
,
348 sizeof(g_shared_policy_size
));
349 g_shared_policy_size
= 0;
350 if (SBOX_ALL_OK
!= ret
) {
351 return (SBOX_ERROR_GENERIC
== ret
) ?
352 ::GetLastError() : ERROR_INVALID_FUNCTION
;
356 new SharedMemIPCServer(sandbox_process_info_
.process_handle(),
357 sandbox_process_info_
.process_id(),
358 job_
, thread_pool_
, ipc_dispatcher
));
360 if (!ipc_server_
->Init(shared_memory
, shared_IPC_size
, kIPCChannelSize
))
361 return ERROR_NOT_ENOUGH_MEMORY
;
363 // After this point we cannot use this handle anymore.
364 ::CloseHandle(sandbox_process_info_
.TakeThreadHandle());
366 return ERROR_SUCCESS
;
369 void TargetProcess::Terminate() {
370 if (!sandbox_process_info_
.IsValid())
373 ::TerminateProcess(sandbox_process_info_
.process_handle(), 0);
376 TargetProcess
* MakeTestTargetProcess(HANDLE process
, HMODULE base_address
) {
377 TargetProcess
* target
= new TargetProcess(NULL
, NULL
, NULL
, NULL
);
378 PROCESS_INFORMATION process_info
= {};
379 process_info
.hProcess
= process
;
380 target
->sandbox_process_info_
.Set(process_info
);
381 target
->base_address_
= base_address
;
385 } // namespace sandbox