Home | History | Annotate | Download | only in common
      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 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.
     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 
     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 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 
     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   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 
    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   base::string16 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   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 
    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