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