• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
MakePathToSys32(const wchar_t * name,bool is_obj_man_path)19 base::string16 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 base::string16();
23 
24   base::string16 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.
MakePathToSysWow64(const wchar_t * name,bool is_obj_man_path)37 base::string16 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 base::string16();
41 
42   base::string16 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 
IsProcessRunning(HANDLE process)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 
MakePathToSys(const wchar_t * name,bool is_obj_man_path)65 base::string16 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 
GetBroker()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 
TestRunner(JobLevel job_level,TokenLevel startup_token,TokenLevel main_token)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 
TestRunner()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 
Init(JobLevel job_level,TokenLevel startup_token,TokenLevel main_token)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 
GetPolicy()127 TargetPolicy* TestRunner::GetPolicy() {
128   return policy_;
129 }
130 
~TestRunner()131 TestRunner::~TestRunner() {
132   if (target_process_ && kill_on_destruction_)
133     ::TerminateProcess(target_process_, 0);
134 
135   if (policy_)
136     policy_->Release();
137 }
138 
AddRule(TargetPolicy::SubSystem subsystem,TargetPolicy::Semantics semantics,const wchar_t * pattern)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 
AddRuleSys32(TargetPolicy::Semantics semantics,const wchar_t * pattern)148 bool TestRunner::AddRuleSys32(TargetPolicy::Semantics semantics,
149                               const wchar_t* pattern) {
150   if (!is_init_)
151     return false;
152 
153   base::string16 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 
AddFsRule(TargetPolicy::Semantics semantics,const wchar_t * pattern)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 
RunTest(const wchar_t * command)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   base::string16 full_command(state_number);
187   full_command += L" ";
188   full_command += command;
189 
190   return InternalRunTest(full_command.c_str());
191 }
192 
InternalRunTest(const wchar_t * command)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   base::string16 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 
SetTimeout(DWORD timeout_ms)269 void TestRunner::SetTimeout(DWORD timeout_ms) {
270   timeout_ = timeout_ms;
271 }
272 
SetTestState(SboxTestsState desired_state)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.
DispatchCall(int argc,wchar_t ** argv)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
337