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