- add sources.
[platform/framework/web/crosswalk.git] / src / sandbox / win / tests / common / controller.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/tests/common/controller.h"
6
7 #include <string>
8
9 #include "base/process/process.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "base/win/windows_version.h"
12 #include "sandbox/win/src/sandbox_factory.h"
13
14 namespace {
15
16 static const int kDefaultTimeout = 60000;
17
18 // Constructs a full path to a file inside the system32 folder.
19 std::wstring MakePathToSys32(const wchar_t* name, bool is_obj_man_path) {
20   wchar_t windows_path[MAX_PATH] = {0};
21   if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH))
22     return std::wstring();
23
24   std::wstring full_path(windows_path);
25   if (full_path.empty())
26     return full_path;
27
28   if (is_obj_man_path)
29     full_path.insert(0, L"\\??\\");
30
31   full_path += L"\\system32\\";
32   full_path += name;
33   return full_path;
34 }
35
36 // Constructs a full path to a file inside the syswow64 folder.
37 std::wstring MakePathToSysWow64(const wchar_t* name, bool is_obj_man_path) {
38   wchar_t windows_path[MAX_PATH] = {0};
39   if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH))
40     return std::wstring();
41
42   std::wstring full_path(windows_path);
43   if (full_path.empty())
44     return full_path;
45
46   if (is_obj_man_path)
47     full_path.insert(0, L"\\??\\");
48
49   full_path += L"\\SysWOW64\\";
50   full_path += name;
51   return full_path;
52 }
53
54 bool IsProcessRunning(HANDLE process) {
55   DWORD exit_code = 0;
56   if (::GetExitCodeProcess(process, &exit_code))
57     return exit_code == STILL_ACTIVE;
58   return false;
59 }
60
61 }  // namespace
62
63 namespace sandbox {
64
65 std::wstring MakePathToSys(const wchar_t* name, bool is_obj_man_path) {
66   return (base::win::OSInfo::GetInstance()->wow64_status() ==
67       base::win::OSInfo::WOW64_ENABLED) ?
68       MakePathToSysWow64(name, is_obj_man_path) :
69       MakePathToSys32(name, is_obj_man_path);
70 }
71
72 BrokerServices* GetBroker() {
73   static BrokerServices* broker = SandboxFactory::GetBrokerServices();
74   static bool is_initialized = false;
75
76   if (!broker) {
77     return NULL;
78   }
79
80   if (!is_initialized) {
81     if (SBOX_ALL_OK != broker->Init())
82       return NULL;
83
84     is_initialized = true;
85   }
86
87   return broker;
88 }
89
90 TestRunner::TestRunner(JobLevel job_level, TokenLevel startup_token,
91                        TokenLevel main_token)
92     : is_init_(false), is_async_(false), no_sandbox_(false),
93       target_process_id_(0) {
94   Init(job_level, startup_token, main_token);
95 }
96
97 TestRunner::TestRunner()
98     : is_init_(false), is_async_(false), no_sandbox_(false),
99       target_process_id_(0) {
100   Init(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
101 }
102
103 void TestRunner::Init(JobLevel job_level, TokenLevel startup_token,
104                       TokenLevel main_token) {
105   broker_ = NULL;
106   policy_ = NULL;
107   timeout_ = kDefaultTimeout;
108   state_ = AFTER_REVERT;
109   is_async_= false;
110   kill_on_destruction_ = true;
111   target_process_id_ = 0;
112
113   broker_ = GetBroker();
114   if (!broker_)
115     return;
116
117   policy_ = broker_->CreatePolicy();
118   if (!policy_)
119     return;
120
121   policy_->SetJobLevel(job_level, 0);
122   policy_->SetTokenLevel(startup_token, main_token);
123
124   is_init_ = true;
125 }
126
127 TargetPolicy* TestRunner::GetPolicy() {
128   return policy_;
129 }
130
131 TestRunner::~TestRunner() {
132   if (target_process_ && kill_on_destruction_)
133     ::TerminateProcess(target_process_, 0);
134
135   if (policy_)
136     policy_->Release();
137 }
138
139 bool TestRunner::AddRule(TargetPolicy::SubSystem subsystem,
140                          TargetPolicy::Semantics semantics,
141                          const wchar_t* pattern) {
142   if (!is_init_)
143     return false;
144
145   return (SBOX_ALL_OK == policy_->AddRule(subsystem, semantics, pattern));
146 }
147
148 bool TestRunner::AddRuleSys32(TargetPolicy::Semantics semantics,
149                               const wchar_t* pattern) {
150   if (!is_init_)
151     return false;
152
153   std::wstring win32_path = MakePathToSys32(pattern, false);
154   if (win32_path.empty())
155     return false;
156
157   if (!AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str()))
158     return false;
159
160   if (base::win::OSInfo::GetInstance()->wow64_status() !=
161       base::win::OSInfo::WOW64_ENABLED)
162     return true;
163
164   win32_path = MakePathToSysWow64(pattern, false);
165   if (win32_path.empty())
166     return false;
167
168   return AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str());
169 }
170
171 bool TestRunner::AddFsRule(TargetPolicy::Semantics semantics,
172                            const wchar_t* pattern) {
173   if (!is_init_)
174     return false;
175
176   return AddRule(TargetPolicy::SUBSYS_FILES, semantics, pattern);
177 }
178
179 int TestRunner::RunTest(const wchar_t* command) {
180   if (MAX_STATE > 10)
181     return SBOX_TEST_INVALID_PARAMETER;
182
183   wchar_t state_number[2];
184   state_number[0] = L'0' + state_;
185   state_number[1] = L'\0';
186   std::wstring full_command(state_number);
187   full_command += L" ";
188   full_command += command;
189
190   return InternalRunTest(full_command.c_str());
191 }
192
193 int TestRunner::InternalRunTest(const wchar_t* command) {
194   if (!is_init_)
195     return SBOX_TEST_FAILED_TO_RUN_TEST;
196
197   // For simplicity TestRunner supports only one process per instance.
198   if (target_process_) {
199     if (IsProcessRunning(target_process_))
200       return SBOX_TEST_FAILED_TO_RUN_TEST;
201     target_process_.Close();
202     target_process_id_ = 0;
203   }
204
205   // Get the path to the sandboxed process.
206   wchar_t prog_name[MAX_PATH];
207   GetModuleFileNameW(NULL, prog_name, MAX_PATH);
208
209   // Launch the sandboxed process.
210   ResultCode result = SBOX_ALL_OK;
211   PROCESS_INFORMATION target = {0};
212
213   std::wstring arguments(L"\"");
214   arguments += prog_name;
215   arguments += L"\" -child";
216   arguments += no_sandbox_ ? L"-no-sandbox " : L" ";
217   arguments += command;
218
219   if (no_sandbox_) {
220     STARTUPINFO startup_info = {sizeof(STARTUPINFO)};
221     if (!::CreateProcessW(prog_name, &arguments[0], NULL, NULL, FALSE, 0,
222                           NULL, NULL, &startup_info, &target)) {
223       return SBOX_ERROR_GENERIC;
224     }
225     broker_->AddTargetPeer(target.hProcess);
226   } else {
227     result = broker_->SpawnTarget(prog_name, arguments.c_str(), policy_,
228                                   &target);
229   }
230
231   if (SBOX_ALL_OK != result)
232     return SBOX_TEST_FAILED_TO_RUN_TEST;
233
234   ::ResumeThread(target.hThread);
235
236   // For an asynchronous run we don't bother waiting.
237   if (is_async_) {
238     target_process_.Set(target.hProcess);
239     target_process_id_ = target.dwProcessId;
240     ::CloseHandle(target.hThread);
241     return SBOX_TEST_SUCCEEDED;
242   }
243
244   if (::IsDebuggerPresent()) {
245     // Don't kill the target process on a time-out while we are debugging.
246     timeout_ = INFINITE;
247   }
248
249   if (WAIT_TIMEOUT == ::WaitForSingleObject(target.hProcess, timeout_)) {
250     ::TerminateProcess(target.hProcess, SBOX_TEST_TIMED_OUT);
251     ::CloseHandle(target.hProcess);
252     ::CloseHandle(target.hThread);
253     return SBOX_TEST_TIMED_OUT;
254   }
255
256   DWORD exit_code = SBOX_TEST_LAST_RESULT;
257   if (!::GetExitCodeProcess(target.hProcess, &exit_code)) {
258     ::CloseHandle(target.hProcess);
259     ::CloseHandle(target.hThread);
260     return SBOX_TEST_FAILED_TO_RUN_TEST;
261   }
262
263   ::CloseHandle(target.hProcess);
264   ::CloseHandle(target.hThread);
265
266   return exit_code;
267 }
268
269 void TestRunner::SetTimeout(DWORD timeout_ms) {
270   timeout_ = timeout_ms;
271 }
272
273 void TestRunner::SetTestState(SboxTestsState desired_state) {
274   state_ = desired_state;
275 }
276
277 // This is the main procedure for the target (child) application. We'll find out
278 // the target test and call it.
279 // We expect the arguments to be:
280 //  argv[1] = "-child"
281 //  argv[2] = SboxTestsState when to run the command
282 //  argv[3] = command to run
283 //  argv[4...] = command arguments.
284 int DispatchCall(int argc, wchar_t **argv) {
285   if (argc < 4)
286     return SBOX_TEST_INVALID_PARAMETER;
287
288   // We hard code two tests to avoid dispatch failures.
289   if (0 == _wcsicmp(argv[3], L"wait")) {
290       Sleep(INFINITE);
291       return SBOX_TEST_TIMED_OUT;
292   }
293
294   if (0 == _wcsicmp(argv[3], L"ping"))
295       return SBOX_TEST_PING_OK;
296
297   SboxTestsState state = static_cast<SboxTestsState>(_wtoi(argv[2]));
298   if ((state <= MIN_STATE) || (state >= MAX_STATE))
299     return SBOX_TEST_INVALID_PARAMETER;
300
301   HMODULE module;
302   if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
303                              GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
304                          reinterpret_cast<wchar_t*>(&DispatchCall), &module))
305     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
306
307   std::string command_name = base::SysWideToMultiByte(argv[3], CP_UTF8);
308   CommandFunction command = reinterpret_cast<CommandFunction>(
309                                 ::GetProcAddress(module, command_name.c_str()));
310   if (!command)
311     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
312
313   if (BEFORE_INIT == state)
314     return command(argc - 4, argv + 4);
315   else if (EVERY_STATE == state)
316     command(argc - 4, argv + 4);
317
318   TargetServices* target = SandboxFactory::GetTargetServices();
319   if (target) {
320     if (SBOX_ALL_OK != target->Init())
321       return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
322
323     if (BEFORE_REVERT == state)
324       return command(argc - 4, argv + 4);
325     else if (EVERY_STATE == state)
326       command(argc - 4, argv + 4);
327
328     target->LowerToken();
329   } else if (0 != _wcsicmp(argv[1], L"-child-no-sandbox")) {
330     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
331   }
332
333   return command(argc - 4, argv + 4);
334 }
335
336 }  // namespace sandbox