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 // 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
;
164 TEST(PolicyTargetTest
, SetInformationThread
) {
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"));
171 runner
.SetTestState(AFTER_REVERT
);
172 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token"));
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"));
179 TEST(PolicyTargetTest
, OpenThreadToken
) {
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"));
186 runner
.SetTestState(AFTER_REVERT
);
187 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token2"));
190 TEST(PolicyTargetTest
, OpenThreadTokenEx
) {
192 if (base::win::GetVersion() < base::win::VERSION_XP
)
195 runner
.SetTestState(BEFORE_REVERT
);
196 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_token3"));
198 runner
.SetTestState(AFTER_REVERT
);
199 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token3"));
202 TEST(PolicyTargetTest
, OpenThread
) {
204 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_thread")) <<
205 "Opens the current thread";
207 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_thread2")) <<
208 "Creates a new thread and opens it";
211 TEST(PolicyTargetTest
, OpenProcess
) {
213 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_process")) <<
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
221 TEST(PolicyTargetTest
, DesktopPolicy
) {
222 BrokerServices
* broker
= GetBroker();
224 // Precreate the desktop.
225 TargetPolicy
* temp_policy
= broker
->CreatePolicy();
226 temp_policy
->CreateAlternateDesktop(false);
227 temp_policy
->Release();
229 ASSERT_TRUE(broker
!= NULL
);
231 // Get the path to the sandboxed app.
232 wchar_t prog_name
[MAX_PATH
];
233 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
235 base::string16
arguments(L
"\"");
236 arguments
+= prog_name
;
237 arguments
+= L
"\" -child 0 wait"; // Don't care about the "state" argument.
240 ResultCode result
= SBOX_ALL_OK
;
241 base::win::ScopedProcessInformation target
;
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
,
249 base::string16 desktop_name
= policy
->GetAlternateDesktop();
252 EXPECT_EQ(SBOX_ALL_OK
, result
);
253 if (result
== SBOX_ALL_OK
)
254 target
.Set(temp_process_info
);
256 EXPECT_EQ(1, ::ResumeThread(target
.thread_handle()));
258 EXPECT_EQ(WAIT_TIMEOUT
, ::WaitForSingleObject(target
.process_handle(), 2000));
260 EXPECT_NE(::GetThreadDesktop(target
.thread_id()),
261 ::GetThreadDesktop(::GetCurrentThreadId()));
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));
268 ::WaitForSingleObject(target
.process_handle(), INFINITE
);
270 // Close the desktop handle.
271 temp_policy
= broker
->CreatePolicy();
272 temp_policy
->DestroyAlternateDesktop();
273 temp_policy
->Release();
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
);
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
284 TEST(PolicyTargetTest
, WinstaPolicy
) {
285 BrokerServices
* broker
= GetBroker();
287 // Precreate the desktop.
288 TargetPolicy
* temp_policy
= broker
->CreatePolicy();
289 temp_policy
->CreateAlternateDesktop(true);
290 temp_policy
->Release();
292 ASSERT_TRUE(broker
!= NULL
);
294 // Get the path to the sandboxed app.
295 wchar_t prog_name
[MAX_PATH
];
296 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
298 base::string16
arguments(L
"\"");
299 arguments
+= prog_name
;
300 arguments
+= L
"\" -child 0 wait"; // Don't care about the "state" argument.
303 ResultCode result
= SBOX_ALL_OK
;
304 base::win::ScopedProcessInformation target
;
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
,
312 base::string16 desktop_name
= policy
->GetAlternateDesktop();
315 EXPECT_EQ(SBOX_ALL_OK
, result
);
316 if (result
== SBOX_ALL_OK
)
317 target
.Set(temp_process_info
);
319 EXPECT_EQ(1, ::ResumeThread(target
.thread_handle()));
321 EXPECT_EQ(WAIT_TIMEOUT
, ::WaitForSingleObject(target
.process_handle(), 2000));
323 EXPECT_NE(::GetThreadDesktop(target
.thread_id()),
324 ::GetThreadDesktop(::GetCurrentThreadId()));
326 ASSERT_FALSE(desktop_name
.empty());
328 // Make sure there is a backslash, for the window station name.
329 EXPECT_NE(desktop_name
.find_first_of(L
'\\'), base::string16::npos
);
331 // Isolate the desktop name.
332 desktop_name
= desktop_name
.substr(desktop_name
.find_first_of(L
'\\') + 1);
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));
339 ::WaitForSingleObject(target
.process_handle(), INFINITE
);
341 // Close the desktop handle.
342 temp_policy
= broker
->CreatePolicy();
343 temp_policy
->DestroyAlternateDesktop();
344 temp_policy
->Release();
347 } // namespace sandbox