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