Home | History | Annotate | Download | only in src
      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 <memory>
      6 #include <string>
      7 
      8 #include "base/strings/string16.h"
      9 #include "base/strings/sys_string_conversions.h"
     10 #include "base/win/scoped_handle.h"
     11 #include "base/win/scoped_process_information.h"
     12 #include "base/win/windows_version.h"
     13 #include "sandbox/win/src/sandbox.h"
     14 #include "sandbox/win/src/sandbox_factory.h"
     15 #include "sandbox/win/src/sandbox_policy.h"
     16 #include "sandbox/win/tests/common/controller.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 namespace {
     20 
     21 // While the shell API provides better calls than this home brew function
     22 // we use GetSystemWindowsDirectoryW which does not query the registry so
     23 // it is safe to use after revert.
     24 string16 MakeFullPathToSystem32(const wchar_t* name) {
     25   wchar_t windows_path[MAX_PATH] = {0};
     26   ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH);
     27   string16 full_path(windows_path);
     28   if (full_path.empty()) {
     29     return full_path;
     30   }
     31   full_path += L"\\system32\\";
     32   full_path += name;
     33   return full_path;
     34 }
     35 
     36 // Creates a process with the |exe| and |command| parameter using the
     37 // unicode and ascii version of the api.
     38 sandbox::SboxTestResult CreateProcessHelper(const string16& exe,
     39                                             const string16& command) {
     40   base::win::ScopedProcessInformation pi;
     41   STARTUPINFOW si = {sizeof(si)};
     42 
     43   const wchar_t *exe_name = NULL;
     44   if (!exe.empty())
     45     exe_name = exe.c_str();
     46 
     47   const wchar_t *cmd_line = NULL;
     48   if (!command.empty())
     49     cmd_line = command.c_str();
     50 
     51   // Create the process with the unicode version of the API.
     52   sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED;
     53   if (!::CreateProcessW(exe_name, const_cast<wchar_t*>(cmd_line), NULL, NULL,
     54                         FALSE, 0, NULL, NULL, &si, pi.Receive())) {
     55     DWORD last_error = GetLastError();
     56     if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
     57         (ERROR_ACCESS_DENIED == last_error) ||
     58         (ERROR_FILE_NOT_FOUND == last_error)) {
     59       ret1 = sandbox::SBOX_TEST_DENIED;
     60     } else {
     61       ret1 = sandbox::SBOX_TEST_FAILED;
     62     }
     63   } else {
     64     ret1 = sandbox::SBOX_TEST_SUCCEEDED;
     65   }
     66 
     67   pi.Close();
     68 
     69   // Do the same with the ansi version of the api
     70   STARTUPINFOA sia = {sizeof(sia)};
     71   sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED;
     72 
     73   std::string narrow_cmd_line;
     74   if (cmd_line)
     75     narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8);
     76   if (!::CreateProcessA(
     77         exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL,
     78         cmd_line ? const_cast<char*>(narrow_cmd_line.c_str()) : NULL,
     79         NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) {
     80     DWORD last_error = GetLastError();
     81     if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
     82         (ERROR_ACCESS_DENIED == last_error) ||
     83         (ERROR_FILE_NOT_FOUND == last_error)) {
     84       ret2 = sandbox::SBOX_TEST_DENIED;
     85     } else {
     86       ret2 = sandbox::SBOX_TEST_FAILED;
     87     }
     88   } else {
     89     ret2 = sandbox::SBOX_TEST_SUCCEEDED;
     90   }
     91 
     92   if (ret1 == ret2)
     93     return ret1;
     94 
     95   return sandbox::SBOX_TEST_FAILED;
     96 }
     97 
     98 }  // namespace
     99 
    100 namespace sandbox {
    101 
    102 SBOX_TESTS_COMMAND int Process_RunApp1(int argc, wchar_t **argv) {
    103   if (argc != 1) {
    104     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    105   }
    106   if ((NULL == argv) || (NULL == argv[0])) {
    107     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    108   }
    109   string16 path = MakeFullPathToSystem32(argv[0]);
    110 
    111   // TEST 1: Try with the path in the app_name.
    112   return CreateProcessHelper(path, string16());
    113 }
    114 
    115 SBOX_TESTS_COMMAND int Process_RunApp2(int argc, wchar_t **argv) {
    116   if (argc != 1) {
    117     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    118   }
    119   if ((NULL == argv) || (NULL == argv[0])) {
    120     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    121   }
    122   string16 path = MakeFullPathToSystem32(argv[0]);
    123 
    124   // TEST 2: Try with the path in the cmd_line.
    125   string16 cmd_line = L"\"";
    126   cmd_line += path;
    127   cmd_line += L"\"";
    128   return CreateProcessHelper(string16(), cmd_line);
    129 }
    130 
    131 SBOX_TESTS_COMMAND int Process_RunApp3(int argc, wchar_t **argv) {
    132   if (argc != 1) {
    133     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    134   }
    135   if ((NULL == argv) || (NULL == argv[0])) {
    136     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    137   }
    138 
    139   // TEST 3: Try file name in the cmd_line.
    140   return CreateProcessHelper(string16(), argv[0]);
    141 }
    142 
    143 SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t **argv) {
    144   if (argc != 1) {
    145     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    146   }
    147   if ((NULL == argv) || (NULL == argv[0])) {
    148     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    149   }
    150 
    151   // TEST 4: Try file name in the app_name and current directory sets correctly.
    152   string16 system32 = MakeFullPathToSystem32(L"");
    153   wchar_t current_directory[MAX_PATH + 1];
    154   int result4;
    155   bool test_succeeded = false;
    156   DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
    157   if (!ret)
    158     return SBOX_TEST_FIRST_ERROR;
    159 
    160   if (ret < MAX_PATH) {
    161     current_directory[ret] = L'\\';
    162     current_directory[ret+1] = L'\0';
    163     if (::SetCurrentDirectory(system32.c_str())) {
    164       result4 = CreateProcessHelper(argv[0], string16());
    165       if (::SetCurrentDirectory(current_directory)) {
    166         test_succeeded = true;
    167       }
    168     } else {
    169       return SBOX_TEST_SECOND_ERROR;
    170     }
    171   }
    172   if (!test_succeeded)
    173     result4 = SBOX_TEST_FAILED;
    174 
    175   return result4;
    176 }
    177 
    178 SBOX_TESTS_COMMAND int Process_RunApp5(int argc, wchar_t **argv) {
    179   if (argc != 1) {
    180     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    181   }
    182   if ((NULL == argv) || (NULL == argv[0])) {
    183     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    184   }
    185   string16 path = MakeFullPathToSystem32(argv[0]);
    186 
    187   // TEST 5: Try with the path in the cmd_line and arguments.
    188   string16 cmd_line = L"\"";
    189   cmd_line += path;
    190   cmd_line += L"\" /I";
    191   return CreateProcessHelper(string16(), cmd_line);
    192 }
    193 
    194 SBOX_TESTS_COMMAND int Process_RunApp6(int argc, wchar_t **argv) {
    195   if (argc != 1) {
    196     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    197   }
    198   if ((NULL == argv) || (NULL == argv[0])) {
    199     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    200   }
    201 
    202   // TEST 6: Try with the file_name in the cmd_line and arguments.
    203   string16 cmd_line = argv[0];
    204   cmd_line += L" /I";
    205   return CreateProcessHelper(string16(), cmd_line);
    206 }
    207 
    208 // Creates a process and checks if it's possible to get a handle to it's token.
    209 SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) {
    210   if (argc != 1)
    211     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    212 
    213   if ((NULL == argv) || (NULL == argv[0]))
    214     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
    215 
    216   string16 path = MakeFullPathToSystem32(argv[0]);
    217 
    218   base::win::ScopedProcessInformation pi;
    219   STARTUPINFOW si = {sizeof(si)};
    220 
    221   if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED,
    222                         NULL, NULL, &si, pi.Receive())) {
    223       return SBOX_TEST_FAILED;
    224   }
    225 
    226   HANDLE token = NULL;
    227   BOOL result =
    228       ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token);
    229   DWORD error = ::GetLastError();
    230 
    231   base::win::ScopedHandle token_handle(token);
    232 
    233   if (!::TerminateProcess(pi.process_handle(), 0))
    234     return SBOX_TEST_FAILED;
    235 
    236   if (result && token)
    237     return SBOX_TEST_SUCCEEDED;
    238 
    239   if (ERROR_ACCESS_DENIED == error)
    240     return SBOX_TEST_DENIED;
    241 
    242   return SBOX_TEST_FAILED;
    243 }
    244 
    245 
    246 SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t **argv) {
    247   HANDLE token;
    248   if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
    249     if (ERROR_ACCESS_DENIED == ::GetLastError()) {
    250       return SBOX_TEST_DENIED;
    251     }
    252   } else {
    253     ::CloseHandle(token);
    254     return SBOX_TEST_SUCCEEDED;
    255   }
    256 
    257   return SBOX_TEST_FAILED;
    258 }
    259 
    260 TEST(ProcessPolicyTest, TestAllAccess) {
    261   // Check if the "all access" rule fails to be added when the token is too
    262   // powerful.
    263   TestRunner runner;
    264 
    265   // Check the failing case.
    266   runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
    267   EXPECT_EQ(SBOX_ERROR_UNSUPPORTED,
    268             runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
    269                                         TargetPolicy::PROCESS_ALL_EXEC,
    270                                         L"this is not important"));
    271 
    272   // Check the working case.
    273   runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE);
    274 
    275   EXPECT_EQ(SBOX_ALL_OK,
    276             runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
    277                                         TargetPolicy::PROCESS_ALL_EXEC,
    278                                         L"this is not important"));
    279 }
    280 
    281 TEST(ProcessPolicyTest, CreateProcessAW) {
    282   TestRunner runner;
    283   string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
    284   string16 system32 = MakeFullPathToSystem32(L"");
    285   ASSERT_TRUE(!exe_path.empty());
    286   EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
    287                              TargetPolicy::PROCESS_MIN_EXEC,
    288                              exe_path.c_str()));
    289 
    290   // Need to add directory rules for the directories that we use in
    291   // SetCurrentDirectory.
    292   EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY,
    293                                system32.c_str()));
    294 
    295   wchar_t current_directory[MAX_PATH];
    296   DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
    297   ASSERT_TRUE(0 != ret && ret < MAX_PATH);
    298 
    299   wcscat_s(current_directory, MAX_PATH, L"\\");
    300   EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY,
    301                                current_directory));
    302 
    303   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp1 calc.exe"));
    304   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp2 calc.exe"));
    305   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp3 calc.exe"));
    306   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp5 calc.exe"));
    307   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp6 calc.exe"));
    308 
    309   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
    310             runner.RunTest(L"Process_RunApp1 findstr.exe"));
    311   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
    312             runner.RunTest(L"Process_RunApp2 findstr.exe"));
    313   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
    314             runner.RunTest(L"Process_RunApp3 findstr.exe"));
    315   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
    316             runner.RunTest(L"Process_RunApp5 findstr.exe"));
    317   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
    318             runner.RunTest(L"Process_RunApp6 findstr.exe"));
    319 
    320 #if !defined(_WIN64)
    321   if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) {
    322     // WinXP results are not reliable.
    323     EXPECT_EQ(SBOX_TEST_SECOND_ERROR,
    324         runner.RunTest(L"Process_RunApp4 calc.exe"));
    325     EXPECT_EQ(SBOX_TEST_SECOND_ERROR,
    326         runner.RunTest(L"Process_RunApp4 findstr.exe"));
    327   }
    328 #endif
    329 }
    330 
    331 TEST(ProcessPolicyTest, OpenToken) {
    332   TestRunner runner;
    333   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken"));
    334 }
    335 
    336 TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) {
    337   TestRunner runner;
    338   string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
    339   ASSERT_TRUE(!exe_path.empty());
    340   EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
    341                              TargetPolicy::PROCESS_MIN_EXEC,
    342                              exe_path.c_str()));
    343 
    344   EXPECT_EQ(SBOX_TEST_DENIED,
    345             runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
    346 }
    347 
    348 TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) {
    349   TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE);
    350   string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
    351   ASSERT_TRUE(!exe_path.empty());
    352   EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
    353                              TargetPolicy::PROCESS_ALL_EXEC,
    354                              exe_path.c_str()));
    355 
    356   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
    357             runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
    358 }
    359 
    360 TEST(ProcessPolicyTest, TestGetProcessTokenMinAccessNoJob) {
    361   TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
    362   string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
    363   ASSERT_TRUE(!exe_path.empty());
    364   EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
    365                              TargetPolicy::PROCESS_MIN_EXEC,
    366                              exe_path.c_str()));
    367 
    368   EXPECT_EQ(SBOX_TEST_DENIED,
    369             runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
    370 }
    371 
    372 TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccessNoJob) {
    373   TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
    374   string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
    375   ASSERT_TRUE(!exe_path.empty());
    376   EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
    377                              TargetPolicy::PROCESS_ALL_EXEC,
    378                              exe_path.c_str()));
    379 
    380   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
    381             runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
    382 }
    383 
    384 }  // namespace sandbox
    385