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 337