Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / sandbox / win / src / target_process.cc
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_.Get(),
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_.Get())) {
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_.Get(),
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_.Get(),
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