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.
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"
16 #define BINDNTDLL(name) \
17 name ## Function name = reinterpret_cast<name ## Function>( \
18 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
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
) {
24 // Get the thread token, using impersonation.
25 if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE
|
26 TOKEN_DUPLICATE
, FALSE
, &thread_token
))
27 return ::GetLastError();
30 ::CloseHandle(thread_token
);
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
);
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
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();
51 if (!::SetThreadToken(NULL
, thread_token
))
52 return ::GetLastError();
54 // See if we fake the call again.
55 int ret
= PolicyTargetTest_token(argc
, argv
);
56 ::CloseHandle(thread_token
);
62 // Opens the thread token with and without impersonation.
63 SBOX_TESTS_COMMAND
int PolicyTargetTest_token2(int argc
, wchar_t **argv
) {
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
);
71 // Get the thread token, without impersonation.
72 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE
| TOKEN_DUPLICATE
,
74 return ::GetLastError();
75 ::CloseHandle(thread_token
);
76 return SBOX_TEST_SUCCEEDED
;
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
;
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
;
96 ::CloseHandle(thread_token
);
98 // Get the thread token, without impersonation.
99 status
= NtOpenThreadTokenEx(GetCurrentThread(),
100 TOKEN_IMPERSONATE
| TOKEN_DUPLICATE
, TRUE
, 0,
102 if (!NT_SUCCESS(status
))
103 return SBOX_TEST_FAILED
;
105 ::CloseHandle(thread_token
);
106 return SBOX_TEST_SUCCEEDED
;
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
);
114 return ::GetLastError();
115 if (!::CloseHandle(thread
))
116 return ::GetLastError();
118 return SBOX_TEST_SUCCEEDED
;
121 // New thread entry point: do nothing.
122 DWORD WINAPI
PolicyTargetTest_thread_main(void* param
) {
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.
131 HANDLE thread
= ::CreateThread(NULL
, 0, &PolicyTargetTest_thread_main
, 0, 0,
134 return ::GetLastError();
135 if (!::CloseHandle(thread
))
136 return ::GetLastError();
138 thread
= ::OpenThread(SYNCHRONIZE
, FALSE
, thread_id
);
140 return ::GetLastError();
142 if (!::CloseHandle(thread
))
143 return ::GetLastError();
145 return SBOX_TEST_SUCCEEDED
;
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
;
161 TEST(PolicyTargetTest
, SetInformationThread
) {
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"));
168 runner
.SetTestState(AFTER_REVERT
);
169 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token"));
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"));
176 TEST(PolicyTargetTest
, OpenThreadToken
) {
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"));
183 runner
.SetTestState(AFTER_REVERT
);
184 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token2"));
187 TEST(PolicyTargetTest
, OpenThreadTokenEx
) {
189 if (base::win::GetVersion() < base::win::VERSION_XP
)
192 runner
.SetTestState(BEFORE_REVERT
);
193 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_token3"));
195 runner
.SetTestState(AFTER_REVERT
);
196 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token3"));
199 TEST(PolicyTargetTest
, OpenThread
) {
201 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_thread")) <<
202 "Opens the current thread";
204 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_thread2")) <<
205 "Creates a new thread and opens it";
208 TEST(PolicyTargetTest
, OpenProcess
) {
210 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_process")) <<
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
218 TEST(PolicyTargetTest
, DesktopPolicy
) {
219 BrokerServices
* broker
= GetBroker();
221 // Precreate the desktop.
222 TargetPolicy
* temp_policy
= broker
->CreatePolicy();
223 temp_policy
->CreateAlternateDesktop(false);
224 temp_policy
->Release();
226 ASSERT_TRUE(broker
!= NULL
);
228 // Get the path to the sandboxed app.
229 wchar_t prog_name
[MAX_PATH
];
230 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
232 std::wstring
arguments(L
"\"");
233 arguments
+= prog_name
;
234 arguments
+= L
"\" -child 0 wait"; // Don't care about the "state" argument.
237 ResultCode result
= SBOX_ALL_OK
;
238 base::win::ScopedProcessInformation target
;
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
,
248 EXPECT_EQ(SBOX_ALL_OK
, result
);
249 if (result
== SBOX_ALL_OK
)
250 target
.Set(temp_process_info
);
252 EXPECT_EQ(1, ::ResumeThread(target
.thread_handle()));
254 EXPECT_EQ(WAIT_TIMEOUT
, ::WaitForSingleObject(target
.process_handle(), 2000));
256 EXPECT_NE(::GetThreadDesktop(target
.thread_id()),
257 ::GetThreadDesktop(::GetCurrentThreadId()));
259 std::wstring 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));
265 ::WaitForSingleObject(target
.process_handle(), INFINITE
);
267 // Close the desktop handle.
268 temp_policy
= broker
->CreatePolicy();
269 temp_policy
->DestroyAlternateDesktop();
270 temp_policy
->Release();
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
);
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
281 TEST(PolicyTargetTest
, WinstaPolicy
) {
282 BrokerServices
* broker
= GetBroker();
284 // Precreate the desktop.
285 TargetPolicy
* temp_policy
= broker
->CreatePolicy();
286 temp_policy
->CreateAlternateDesktop(true);
287 temp_policy
->Release();
289 ASSERT_TRUE(broker
!= NULL
);
291 // Get the path to the sandboxed app.
292 wchar_t prog_name
[MAX_PATH
];
293 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
295 std::wstring
arguments(L
"\"");
296 arguments
+= prog_name
;
297 arguments
+= L
"\" -child 0 wait"; // Don't care about the "state" argument.
300 ResultCode result
= SBOX_ALL_OK
;
301 base::win::ScopedProcessInformation target
;
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
,
311 EXPECT_EQ(SBOX_ALL_OK
, result
);
312 if (result
== SBOX_ALL_OK
)
313 target
.Set(temp_process_info
);
315 EXPECT_EQ(1, ::ResumeThread(target
.thread_handle()));
317 EXPECT_EQ(WAIT_TIMEOUT
, ::WaitForSingleObject(target
.process_handle(), 2000));
319 EXPECT_NE(::GetThreadDesktop(target
.thread_id()),
320 ::GetThreadDesktop(::GetCurrentThreadId()));
322 std::wstring desktop_name
= policy
->GetAlternateDesktop();
323 ASSERT_FALSE(desktop_name
.empty());
325 // Make sure there is a backslash, for the window station name.
326 EXPECT_NE(desktop_name
.find_first_of(L
'\\'), std::wstring::npos
);
328 // Isolate the desktop name.
329 desktop_name
= desktop_name
.substr(desktop_name
.find_first_of(L
'\\') + 1);
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));
336 ::WaitForSingleObject(target
.process_handle(), INFINITE
);
338 // Close the desktop handle.
339 temp_policy
= broker
->CreatePolicy();
340 temp_policy
->DestroyAlternateDesktop();
341 temp_policy
->Release();
344 } // namespace sandbox