| 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 |