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 "base/win/scoped_process_information.h"
      6 #include "base/win/windows_version.h"
      7 #include "sandbox/win/src/sandbox.h"
      8 #include "sandbox/win/src/sandbox_factory.h"
      9 #include "sandbox/win/src/sandbox_utils.h"
     10 #include "sandbox/win/src/target_services.h"
     11 #include "sandbox/win/tests/common/controller.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 
     14 namespace sandbox {
     15 
     16 #define BINDNTDLL(name) \
     17     name ## Function name = reinterpret_cast<name ## Function>( \
     18       ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
     19 
     20 // Reverts to self and verify that SetInformationToken was faked. Returns
     21 // SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked.
     22 SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t **argv) {
     23   HANDLE thread_token;
     24   // Get the thread token, using impersonation.
     25   if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE |
     26                              TOKEN_DUPLICATE, FALSE, &thread_token))
     27     return ::GetLastError();
     28 
     29   ::RevertToSelf();
     30   ::CloseHandle(thread_token);
     31 
     32   int ret = SBOX_TEST_FAILED;
     33   if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
     34                         FALSE, &thread_token)) {
     35     ret = SBOX_TEST_SUCCEEDED;
     36     ::CloseHandle(thread_token);
     37   }
     38   return ret;
     39 }
     40 
     41 // Stores the high privilege token on a static variable, change impersonation
     42 // again to that one and verify that we are not interfering anymore with
     43 // RevertToSelf.
     44 SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t **argv) {
     45   static HANDLE thread_token;
     46   if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
     47     if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE |
     48                                TOKEN_DUPLICATE, FALSE, &thread_token))
     49       return ::GetLastError();
     50   } else {
     51     if (!::SetThreadToken(NULL, thread_token))
     52       return ::GetLastError();
     53 
     54     // See if we fake the call again.
     55     int ret = PolicyTargetTest_token(argc, argv);
     56     ::CloseHandle(thread_token);
     57     return ret;
     58   }
     59   return 0;
     60 }
     61 
     62 // Opens the thread token with and without impersonation.
     63 SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t **argv) {
     64   HANDLE thread_token;
     65   // Get the thread token, using impersonation.
     66   if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE |
     67                              TOKEN_DUPLICATE, FALSE, &thread_token))
     68     return ::GetLastError();
     69   ::CloseHandle(thread_token);
     70 
     71   // Get the thread token, without impersonation.
     72   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
     73                        TRUE, &thread_token))
     74     return ::GetLastError();
     75   ::CloseHandle(thread_token);
     76   return SBOX_TEST_SUCCEEDED;
     77 }
     78 
     79 // Opens the thread token with and without impersonation, using
     80 // NtOpenThreadTokenEX.
     81 SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t **argv) {
     82   BINDNTDLL(NtOpenThreadTokenEx);
     83   if (!NtOpenThreadTokenEx)
     84     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
     85 
     86   HANDLE thread_token;
     87   // Get the thread token, using impersonation.
     88   NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(),
     89                                         TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
     90                                         FALSE, 0, &thread_token);
     91   if (status == STATUS_NO_TOKEN)
     92     return ERROR_NO_TOKEN;
     93   if (!NT_SUCCESS(status))
     94     return SBOX_TEST_FAILED;
     95 
     96   ::CloseHandle(thread_token);
     97 
     98   // Get the thread token, without impersonation.
     99   status = NtOpenThreadTokenEx(GetCurrentThread(),
    100                                TOKEN_IMPERSONATE | TOKEN_DUPLICATE, TRUE, 0,
    101                                &thread_token);
    102   if (!NT_SUCCESS(status))
    103     return SBOX_TEST_FAILED;
    104 
    105   ::CloseHandle(thread_token);
    106   return SBOX_TEST_SUCCEEDED;
    107 }
    108 
    109 // Tests that we can open the current thread.
    110 SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t **argv) {
    111   DWORD thread_id = ::GetCurrentThreadId();
    112   HANDLE thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id);
    113   if (!thread)
    114     return ::GetLastError();
    115   if (!::CloseHandle(thread))
    116     return ::GetLastError();
    117 
    118   return SBOX_TEST_SUCCEEDED;
    119 }
    120 
    121 // New thread entry point: do  nothing.
    122 DWORD WINAPI PolicyTargetTest_thread_main(void* param) {
    123   ::Sleep(INFINITE);
    124   return 0;
    125 }
    126 
    127 // Tests that we can create a new thread, and open it.
    128 SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t **argv) {
    129   // Use default values to create a new thread.
    130   DWORD thread_id;
    131   HANDLE thread = ::CreateThread(NULL, 0, &PolicyTargetTest_thread_main, 0, 0,
    132                                  &thread_id);
    133   if (!thread)
    134     return ::GetLastError();
    135   if (!::CloseHandle(thread))
    136     return ::GetLastError();
    137 
    138   thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id);
    139   if (!thread)
    140     return ::GetLastError();
    141 
    142   if (!::CloseHandle(thread))
    143     return ::GetLastError();
    144 
    145   return SBOX_TEST_SUCCEEDED;
    146 }
    147 
    148 // Tests that we can call CreateProcess.
    149 SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t **argv) {
    150   // Use default values to create a new process.
    151   STARTUPINFO startup_info = {0};
    152   startup_info.cb = sizeof(startup_info);
    153   PROCESS_INFORMATION temp_process_info = {};
    154   // Note: CreateProcessW() can write to its lpCommandLine, don't pass a
    155   // raw string literal.
    156   base::string16 writable_cmdline_str(L"foo.exe");
    157   if (!::CreateProcessW(L"foo.exe", &writable_cmdline_str[0], NULL, NULL, FALSE,
    158                         0, NULL, NULL, &startup_info, &temp_process_info))
    159     return SBOX_TEST_SUCCEEDED;
    160   base::win::ScopedProcessInformation process_info(temp_process_info);
    161   return SBOX_TEST_FAILED;
    162 }
    163 
    164 TEST(PolicyTargetTest, SetInformationThread) {
    165   TestRunner runner;
    166   if (base::win::GetVersion() >= base::win::VERSION_XP) {
    167     runner.SetTestState(BEFORE_REVERT);
    168     EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token"));
    169   }
    170 
    171   runner.SetTestState(AFTER_REVERT);
    172   EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token"));
    173 
    174   runner.SetTestState(EVERY_STATE);
    175   if (base::win::GetVersion() >= base::win::VERSION_XP)
    176     EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal"));
    177 }
    178 
    179 TEST(PolicyTargetTest, OpenThreadToken) {
    180   TestRunner runner;
    181   if (base::win::GetVersion() >= base::win::VERSION_XP) {
    182     runner.SetTestState(BEFORE_REVERT);
    183     EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2"));
    184   }
    185 
    186   runner.SetTestState(AFTER_REVERT);
    187   EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2"));
    188 }
    189 
    190 TEST(PolicyTargetTest, OpenThreadTokenEx) {
    191   TestRunner runner;
    192   if (base::win::GetVersion() < base::win::VERSION_XP)
    193     return;
    194 
    195   runner.SetTestState(BEFORE_REVERT);
    196   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3"));
    197 
    198   runner.SetTestState(AFTER_REVERT);
    199   EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3"));
    200 }
    201 
    202 TEST(PolicyTargetTest, OpenThread) {
    203   TestRunner runner;
    204   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread")) <<
    205       "Opens the current thread";
    206 
    207   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2")) <<
    208       "Creates a new thread and opens it";
    209 }
    210 
    211 TEST(PolicyTargetTest, OpenProcess) {
    212   TestRunner runner;
    213   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process")) <<
    214       "Opens a process";
    215 }
    216 
    217 // Launches the app in the sandbox and ask it to wait in an
    218 // infinite loop. Waits for 2 seconds and then check if the
    219 // desktop associated with the app thread is not the same as the
    220 // current desktop.
    221 TEST(PolicyTargetTest, DesktopPolicy) {
    222   BrokerServices* broker = GetBroker();
    223 
    224   // Precreate the desktop.
    225   TargetPolicy* temp_policy = broker->CreatePolicy();
    226   temp_policy->CreateAlternateDesktop(false);
    227   temp_policy->Release();
    228 
    229   ASSERT_TRUE(broker != NULL);
    230 
    231   // Get the path to the sandboxed app.
    232   wchar_t prog_name[MAX_PATH];
    233   GetModuleFileNameW(NULL, prog_name, MAX_PATH);
    234 
    235   base::string16 arguments(L"\"");
    236   arguments += prog_name;
    237   arguments += L"\" -child 0 wait";  // Don't care about the "state" argument.
    238 
    239   // Launch the app.
    240   ResultCode result = SBOX_ALL_OK;
    241   base::win::ScopedProcessInformation target;
    242 
    243   TargetPolicy* policy = broker->CreatePolicy();
    244   policy->SetAlternateDesktop(false);
    245   policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
    246   PROCESS_INFORMATION temp_process_info = {};
    247   result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
    248                                &temp_process_info);
    249   base::string16 desktop_name = policy->GetAlternateDesktop();
    250   policy->Release();
    251 
    252   EXPECT_EQ(SBOX_ALL_OK, result);
    253   if (result == SBOX_ALL_OK)
    254     target.Set(temp_process_info);
    255 
    256   EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
    257 
    258   EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
    259 
    260   EXPECT_NE(::GetThreadDesktop(target.thread_id()),
    261             ::GetThreadDesktop(::GetCurrentThreadId()));
    262 
    263   HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE);
    264   EXPECT_TRUE(NULL != desk);
    265   EXPECT_TRUE(::CloseDesktop(desk));
    266   EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
    267 
    268   ::WaitForSingleObject(target.process_handle(), INFINITE);
    269 
    270   // Close the desktop handle.
    271   temp_policy = broker->CreatePolicy();
    272   temp_policy->DestroyAlternateDesktop();
    273   temp_policy->Release();
    274 
    275   // Make sure the desktop does not exist anymore.
    276   desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE);
    277   EXPECT_TRUE(NULL == desk);
    278 }
    279 
    280 // Launches the app in the sandbox and ask it to wait in an
    281 // infinite loop. Waits for 2 seconds and then check if the
    282 // winstation associated with the app thread is not the same as the
    283 // current desktop.
    284 TEST(PolicyTargetTest, WinstaPolicy) {
    285   BrokerServices* broker = GetBroker();
    286 
    287   // Precreate the desktop.
    288   TargetPolicy* temp_policy = broker->CreatePolicy();
    289   temp_policy->CreateAlternateDesktop(true);
    290   temp_policy->Release();
    291 
    292   ASSERT_TRUE(broker != NULL);
    293 
    294   // Get the path to the sandboxed app.
    295   wchar_t prog_name[MAX_PATH];
    296   GetModuleFileNameW(NULL, prog_name, MAX_PATH);
    297 
    298   base::string16 arguments(L"\"");
    299   arguments += prog_name;
    300   arguments += L"\" -child 0 wait";  // Don't care about the "state" argument.
    301 
    302   // Launch the app.
    303   ResultCode result = SBOX_ALL_OK;
    304   base::win::ScopedProcessInformation target;
    305 
    306   TargetPolicy* policy = broker->CreatePolicy();
    307   policy->SetAlternateDesktop(true);
    308   policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
    309   PROCESS_INFORMATION temp_process_info = {};
    310   result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
    311                                &temp_process_info);
    312   base::string16 desktop_name = policy->GetAlternateDesktop();
    313   policy->Release();
    314 
    315   EXPECT_EQ(SBOX_ALL_OK, result);
    316   if (result == SBOX_ALL_OK)
    317     target.Set(temp_process_info);
    318 
    319   EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
    320 
    321   EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
    322 
    323   EXPECT_NE(::GetThreadDesktop(target.thread_id()),
    324             ::GetThreadDesktop(::GetCurrentThreadId()));
    325 
    326   ASSERT_FALSE(desktop_name.empty());
    327 
    328   // Make sure there is a backslash, for the window station name.
    329   EXPECT_NE(desktop_name.find_first_of(L'\\'), base::string16::npos);
    330 
    331   // Isolate the desktop name.
    332   desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1);
    333 
    334   HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE);
    335   // This should fail if the desktop is really on another window station.
    336   EXPECT_FALSE(NULL != desk);
    337   EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
    338 
    339   ::WaitForSingleObject(target.process_handle(), INFINITE);
    340 
    341   // Close the desktop handle.
    342   temp_policy = broker->CreatePolicy();
    343   temp_policy->DestroyAlternateDesktop();
    344   temp_policy->Release();
    345 }
    346 
    347 }  // namespace sandbox
    348