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 if (!::CreateProcessW(L"foo.exe", L"foo.exe", NULL, NULL, FALSE, 0, 155 NULL, NULL, &startup_info, &temp_process_info)) 156 return SBOX_TEST_SUCCEEDED; 157 base::win::ScopedProcessInformation process_info(temp_process_info); 158 return SBOX_TEST_FAILED; 159 } 160 161 TEST(PolicyTargetTest, SetInformationThread) { 162 TestRunner runner; 163 if (base::win::GetVersion() >= base::win::VERSION_XP) { 164 runner.SetTestState(BEFORE_REVERT); 165 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token")); 166 } 167 168 runner.SetTestState(AFTER_REVERT); 169 EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token")); 170 171 runner.SetTestState(EVERY_STATE); 172 if (base::win::GetVersion() >= base::win::VERSION_XP) 173 EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal")); 174 } 175 176 TEST(PolicyTargetTest, OpenThreadToken) { 177 TestRunner runner; 178 if (base::win::GetVersion() >= base::win::VERSION_XP) { 179 runner.SetTestState(BEFORE_REVERT); 180 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2")); 181 } 182 183 runner.SetTestState(AFTER_REVERT); 184 EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2")); 185 } 186 187 TEST(PolicyTargetTest, OpenThreadTokenEx) { 188 TestRunner runner; 189 if (base::win::GetVersion() < base::win::VERSION_XP) 190 return; 191 192 runner.SetTestState(BEFORE_REVERT); 193 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3")); 194 195 runner.SetTestState(AFTER_REVERT); 196 EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3")); 197 } 198 199 TEST(PolicyTargetTest, OpenThread) { 200 TestRunner runner; 201 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread")) << 202 "Opens the current thread"; 203 204 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2")) << 205 "Creates a new thread and opens it"; 206 } 207 208 TEST(PolicyTargetTest, OpenProcess) { 209 TestRunner runner; 210 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process")) << 211 "Opens a process"; 212 } 213 214 // Launches the app in the sandbox and ask it to wait in an 215 // infinite loop. Waits for 2 seconds and then check if the 216 // desktop associated with the app thread is not the same as the 217 // current desktop. 218 TEST(PolicyTargetTest, DesktopPolicy) { 219 BrokerServices* broker = GetBroker(); 220 221 // Precreate the desktop. 222 TargetPolicy* temp_policy = broker->CreatePolicy(); 223 temp_policy->CreateAlternateDesktop(false); 224 temp_policy->Release(); 225 226 ASSERT_TRUE(broker != NULL); 227 228 // Get the path to the sandboxed app. 229 wchar_t prog_name[MAX_PATH]; 230 GetModuleFileNameW(NULL, prog_name, MAX_PATH); 231 232 base::string16 arguments(L"\""); 233 arguments += prog_name; 234 arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. 235 236 // Launch the app. 237 ResultCode result = SBOX_ALL_OK; 238 base::win::ScopedProcessInformation target; 239 240 TargetPolicy* policy = broker->CreatePolicy(); 241 policy->SetAlternateDesktop(false); 242 policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); 243 PROCESS_INFORMATION temp_process_info = {}; 244 result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, 245 &temp_process_info); 246 policy->Release(); 247 248 EXPECT_EQ(SBOX_ALL_OK, result); 249 if (result == SBOX_ALL_OK) 250 target.Set(temp_process_info); 251 252 EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); 253 254 EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); 255 256 EXPECT_NE(::GetThreadDesktop(target.thread_id()), 257 ::GetThreadDesktop(::GetCurrentThreadId())); 258 259 base::string16 desktop_name = policy->GetAlternateDesktop(); 260 HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); 261 EXPECT_TRUE(NULL != desk); 262 EXPECT_TRUE(::CloseDesktop(desk)); 263 EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); 264 265 ::WaitForSingleObject(target.process_handle(), INFINITE); 266 267 // Close the desktop handle. 268 temp_policy = broker->CreatePolicy(); 269 temp_policy->DestroyAlternateDesktop(); 270 temp_policy->Release(); 271 272 // Make sure the desktop does not exist anymore. 273 desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); 274 EXPECT_TRUE(NULL == desk); 275 } 276 277 // Launches the app in the sandbox and ask it to wait in an 278 // infinite loop. Waits for 2 seconds and then check if the 279 // winstation associated with the app thread is not the same as the 280 // current desktop. 281 TEST(PolicyTargetTest, WinstaPolicy) { 282 BrokerServices* broker = GetBroker(); 283 284 // Precreate the desktop. 285 TargetPolicy* temp_policy = broker->CreatePolicy(); 286 temp_policy->CreateAlternateDesktop(true); 287 temp_policy->Release(); 288 289 ASSERT_TRUE(broker != NULL); 290 291 // Get the path to the sandboxed app. 292 wchar_t prog_name[MAX_PATH]; 293 GetModuleFileNameW(NULL, prog_name, MAX_PATH); 294 295 base::string16 arguments(L"\""); 296 arguments += prog_name; 297 arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. 298 299 // Launch the app. 300 ResultCode result = SBOX_ALL_OK; 301 base::win::ScopedProcessInformation target; 302 303 TargetPolicy* policy = broker->CreatePolicy(); 304 policy->SetAlternateDesktop(true); 305 policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); 306 PROCESS_INFORMATION temp_process_info = {}; 307 result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, 308 &temp_process_info); 309 policy->Release(); 310 311 EXPECT_EQ(SBOX_ALL_OK, result); 312 if (result == SBOX_ALL_OK) 313 target.Set(temp_process_info); 314 315 EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); 316 317 EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); 318 319 EXPECT_NE(::GetThreadDesktop(target.thread_id()), 320 ::GetThreadDesktop(::GetCurrentThreadId())); 321 322 base::string16 desktop_name = policy->GetAlternateDesktop(); 323 ASSERT_FALSE(desktop_name.empty()); 324 325 // Make sure there is a backslash, for the window station name. 326 EXPECT_NE(desktop_name.find_first_of(L'\\'), base::string16::npos); 327 328 // Isolate the desktop name. 329 desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1); 330 331 HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); 332 // This should fail if the desktop is really on another window station. 333 EXPECT_FALSE(NULL != desk); 334 EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); 335 336 ::WaitForSingleObject(target.process_handle(), INFINITE); 337 338 // Close the desktop handle. 339 temp_policy = broker->CreatePolicy(); 340 temp_policy->DestroyAlternateDesktop(); 341 temp_policy->Release(); 342 } 343 344 } // namespace sandbox 345