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/memory/shared_memory.h"
6 #include "base/strings/string_number_conversions.h"
7 #include "base/strings/string_piece.h"
8 #include "base/win/scoped_process_information.h"
9 #include "base/win/windows_version.h"
10 #include "sandbox/win/src/sandbox.h"
11 #include "sandbox/win/src/sandbox_factory.h"
12 #include "sandbox/win/src/sandbox_utils.h"
13 #include "sandbox/win/src/target_services.h"
14 #include "sandbox/win/tests/common/controller.h"
15 #include "testing/gtest/include/gtest/gtest.h"
19 #define BINDNTDLL(name) \
20 name ## Function name = reinterpret_cast<name ## Function>( \
21 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
23 // Reverts to self and verify that SetInformationToken was faked. Returns
24 // SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked.
25 SBOX_TESTS_COMMAND
int PolicyTargetTest_token(int argc
, wchar_t **argv
) {
27 // Get the thread token, using impersonation.
28 if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE
|
29 TOKEN_DUPLICATE
, FALSE
, &thread_token
))
30 return ::GetLastError();
33 ::CloseHandle(thread_token
);
35 int ret
= SBOX_TEST_FAILED
;
36 if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE
| TOKEN_DUPLICATE
,
37 FALSE
, &thread_token
)) {
38 ret
= SBOX_TEST_SUCCEEDED
;
39 ::CloseHandle(thread_token
);
44 // Stores the high privilege token on a static variable, change impersonation
45 // again to that one and verify that we are not interfering anymore with
47 SBOX_TESTS_COMMAND
int PolicyTargetTest_steal(int argc
, wchar_t **argv
) {
48 static HANDLE thread_token
;
49 if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
50 if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE
|
51 TOKEN_DUPLICATE
, FALSE
, &thread_token
))
52 return ::GetLastError();
54 if (!::SetThreadToken(NULL
, thread_token
))
55 return ::GetLastError();
57 // See if we fake the call again.
58 int ret
= PolicyTargetTest_token(argc
, argv
);
59 ::CloseHandle(thread_token
);
65 // Opens the thread token with and without impersonation.
66 SBOX_TESTS_COMMAND
int PolicyTargetTest_token2(int argc
, wchar_t **argv
) {
68 // Get the thread token, using impersonation.
69 if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE
|
70 TOKEN_DUPLICATE
, FALSE
, &thread_token
))
71 return ::GetLastError();
72 ::CloseHandle(thread_token
);
74 // Get the thread token, without impersonation.
75 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE
| TOKEN_DUPLICATE
,
77 return ::GetLastError();
78 ::CloseHandle(thread_token
);
79 return SBOX_TEST_SUCCEEDED
;
82 // Opens the thread token with and without impersonation, using
83 // NtOpenThreadTokenEX.
84 SBOX_TESTS_COMMAND
int PolicyTargetTest_token3(int argc
, wchar_t **argv
) {
85 BINDNTDLL(NtOpenThreadTokenEx
);
86 if (!NtOpenThreadTokenEx
)
87 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
90 // Get the thread token, using impersonation.
91 NTSTATUS status
= NtOpenThreadTokenEx(GetCurrentThread(),
92 TOKEN_IMPERSONATE
| TOKEN_DUPLICATE
,
93 FALSE
, 0, &thread_token
);
94 if (status
== STATUS_NO_TOKEN
)
95 return ERROR_NO_TOKEN
;
96 if (!NT_SUCCESS(status
))
97 return SBOX_TEST_FAILED
;
99 ::CloseHandle(thread_token
);
101 // Get the thread token, without impersonation.
102 status
= NtOpenThreadTokenEx(GetCurrentThread(),
103 TOKEN_IMPERSONATE
| TOKEN_DUPLICATE
, TRUE
, 0,
105 if (!NT_SUCCESS(status
))
106 return SBOX_TEST_FAILED
;
108 ::CloseHandle(thread_token
);
109 return SBOX_TEST_SUCCEEDED
;
112 // Tests that we can open the current thread.
113 SBOX_TESTS_COMMAND
int PolicyTargetTest_thread(int argc
, wchar_t **argv
) {
114 DWORD thread_id
= ::GetCurrentThreadId();
115 HANDLE thread
= ::OpenThread(SYNCHRONIZE
, FALSE
, thread_id
);
117 return ::GetLastError();
118 if (!::CloseHandle(thread
))
119 return ::GetLastError();
121 return SBOX_TEST_SUCCEEDED
;
124 // New thread entry point: do nothing.
125 DWORD WINAPI
PolicyTargetTest_thread_main(void* param
) {
130 // Tests that we can create a new thread, and open it.
131 SBOX_TESTS_COMMAND
int PolicyTargetTest_thread2(int argc
, wchar_t **argv
) {
132 // Use default values to create a new thread.
134 HANDLE thread
= ::CreateThread(NULL
, 0, &PolicyTargetTest_thread_main
, 0, 0,
137 return ::GetLastError();
138 if (!::CloseHandle(thread
))
139 return ::GetLastError();
141 thread
= ::OpenThread(SYNCHRONIZE
, FALSE
, thread_id
);
143 return ::GetLastError();
145 if (!::CloseHandle(thread
))
146 return ::GetLastError();
148 return SBOX_TEST_SUCCEEDED
;
151 // Tests that we can call CreateProcess.
152 SBOX_TESTS_COMMAND
int PolicyTargetTest_process(int argc
, wchar_t **argv
) {
153 // Use default values to create a new process.
154 STARTUPINFO startup_info
= {0};
155 startup_info
.cb
= sizeof(startup_info
);
156 PROCESS_INFORMATION temp_process_info
= {};
157 // Note: CreateProcessW() can write to its lpCommandLine, don't pass a
158 // raw string literal.
159 base::string16
writable_cmdline_str(L
"foo.exe");
160 if (!::CreateProcessW(L
"foo.exe", &writable_cmdline_str
[0], NULL
, NULL
, FALSE
,
161 0, NULL
, NULL
, &startup_info
, &temp_process_info
))
162 return SBOX_TEST_SUCCEEDED
;
163 base::win::ScopedProcessInformation
process_info(temp_process_info
);
164 return SBOX_TEST_FAILED
;
167 TEST(PolicyTargetTest
, SetInformationThread
) {
169 if (base::win::GetVersion() >= base::win::VERSION_XP
) {
170 runner
.SetTestState(BEFORE_REVERT
);
171 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_token"));
174 runner
.SetTestState(AFTER_REVERT
);
175 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token"));
177 runner
.SetTestState(EVERY_STATE
);
178 if (base::win::GetVersion() >= base::win::VERSION_XP
)
179 EXPECT_EQ(SBOX_TEST_FAILED
, runner
.RunTest(L
"PolicyTargetTest_steal"));
182 TEST(PolicyTargetTest
, OpenThreadToken
) {
184 if (base::win::GetVersion() >= base::win::VERSION_XP
) {
185 runner
.SetTestState(BEFORE_REVERT
);
186 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_token2"));
189 runner
.SetTestState(AFTER_REVERT
);
190 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token2"));
193 TEST(PolicyTargetTest
, OpenThreadTokenEx
) {
195 if (base::win::GetVersion() < base::win::VERSION_XP
)
198 runner
.SetTestState(BEFORE_REVERT
);
199 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_token3"));
201 runner
.SetTestState(AFTER_REVERT
);
202 EXPECT_EQ(ERROR_NO_TOKEN
, runner
.RunTest(L
"PolicyTargetTest_token3"));
205 TEST(PolicyTargetTest
, OpenThread
) {
207 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_thread")) <<
208 "Opens the current thread";
210 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_thread2")) <<
211 "Creates a new thread and opens it";
214 TEST(PolicyTargetTest
, OpenProcess
) {
216 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"PolicyTargetTest_process")) <<
220 // Launches the app in the sandbox and ask it to wait in an
221 // infinite loop. Waits for 2 seconds and then check if the
222 // desktop associated with the app thread is not the same as the
224 TEST(PolicyTargetTest
, DesktopPolicy
) {
225 BrokerServices
* broker
= GetBroker();
227 // Precreate the desktop.
228 TargetPolicy
* temp_policy
= broker
->CreatePolicy();
229 temp_policy
->CreateAlternateDesktop(false);
230 temp_policy
->Release();
232 ASSERT_TRUE(broker
!= NULL
);
234 // Get the path to the sandboxed app.
235 wchar_t prog_name
[MAX_PATH
];
236 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
238 base::string16
arguments(L
"\"");
239 arguments
+= prog_name
;
240 arguments
+= L
"\" -child 0 wait"; // Don't care about the "state" argument.
243 ResultCode result
= SBOX_ALL_OK
;
244 base::win::ScopedProcessInformation target
;
246 TargetPolicy
* policy
= broker
->CreatePolicy();
247 policy
->SetAlternateDesktop(false);
248 policy
->SetTokenLevel(USER_INTERACTIVE
, USER_LOCKDOWN
);
249 PROCESS_INFORMATION temp_process_info
= {};
250 result
= broker
->SpawnTarget(prog_name
, arguments
.c_str(), policy
,
252 base::string16 desktop_name
= policy
->GetAlternateDesktop();
255 EXPECT_EQ(SBOX_ALL_OK
, result
);
256 if (result
== SBOX_ALL_OK
)
257 target
.Set(temp_process_info
);
259 EXPECT_EQ(1, ::ResumeThread(target
.thread_handle()));
261 EXPECT_EQ(WAIT_TIMEOUT
, ::WaitForSingleObject(target
.process_handle(), 2000));
263 EXPECT_NE(::GetThreadDesktop(target
.thread_id()),
264 ::GetThreadDesktop(::GetCurrentThreadId()));
266 HDESK desk
= ::OpenDesktop(desktop_name
.c_str(), 0, FALSE
, DESKTOP_ENUMERATE
);
267 EXPECT_TRUE(NULL
!= desk
);
268 EXPECT_TRUE(::CloseDesktop(desk
));
269 EXPECT_TRUE(::TerminateProcess(target
.process_handle(), 0));
271 ::WaitForSingleObject(target
.process_handle(), INFINITE
);
273 // Close the desktop handle.
274 temp_policy
= broker
->CreatePolicy();
275 temp_policy
->DestroyAlternateDesktop();
276 temp_policy
->Release();
278 // Make sure the desktop does not exist anymore.
279 desk
= ::OpenDesktop(desktop_name
.c_str(), 0, FALSE
, DESKTOP_ENUMERATE
);
280 EXPECT_TRUE(NULL
== desk
);
283 // Launches the app in the sandbox and ask it to wait in an
284 // infinite loop. Waits for 2 seconds and then check if the
285 // winstation associated with the app thread is not the same as the
287 TEST(PolicyTargetTest
, WinstaPolicy
) {
288 BrokerServices
* broker
= GetBroker();
290 // Precreate the desktop.
291 TargetPolicy
* temp_policy
= broker
->CreatePolicy();
292 temp_policy
->CreateAlternateDesktop(true);
293 temp_policy
->Release();
295 ASSERT_TRUE(broker
!= NULL
);
297 // Get the path to the sandboxed app.
298 wchar_t prog_name
[MAX_PATH
];
299 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
301 base::string16
arguments(L
"\"");
302 arguments
+= prog_name
;
303 arguments
+= L
"\" -child 0 wait"; // Don't care about the "state" argument.
306 ResultCode result
= SBOX_ALL_OK
;
307 base::win::ScopedProcessInformation target
;
309 TargetPolicy
* policy
= broker
->CreatePolicy();
310 policy
->SetAlternateDesktop(true);
311 policy
->SetTokenLevel(USER_INTERACTIVE
, USER_LOCKDOWN
);
312 PROCESS_INFORMATION temp_process_info
= {};
313 result
= broker
->SpawnTarget(prog_name
, arguments
.c_str(), policy
,
315 base::string16 desktop_name
= policy
->GetAlternateDesktop();
318 EXPECT_EQ(SBOX_ALL_OK
, result
);
319 if (result
== SBOX_ALL_OK
)
320 target
.Set(temp_process_info
);
322 EXPECT_EQ(1, ::ResumeThread(target
.thread_handle()));
324 EXPECT_EQ(WAIT_TIMEOUT
, ::WaitForSingleObject(target
.process_handle(), 2000));
326 EXPECT_NE(::GetThreadDesktop(target
.thread_id()),
327 ::GetThreadDesktop(::GetCurrentThreadId()));
329 ASSERT_FALSE(desktop_name
.empty());
331 // Make sure there is a backslash, for the window station name.
332 EXPECT_NE(desktop_name
.find_first_of(L
'\\'), base::string16::npos
);
334 // Isolate the desktop name.
335 desktop_name
= desktop_name
.substr(desktop_name
.find_first_of(L
'\\') + 1);
337 HDESK desk
= ::OpenDesktop(desktop_name
.c_str(), 0, FALSE
, DESKTOP_ENUMERATE
);
338 // This should fail if the desktop is really on another window station.
339 EXPECT_FALSE(NULL
!= desk
);
340 EXPECT_TRUE(::TerminateProcess(target
.process_handle(), 0));
342 ::WaitForSingleObject(target
.process_handle(), INFINITE
);
344 // Close the desktop handle.
345 temp_policy
= broker
->CreatePolicy();
346 temp_policy
->DestroyAlternateDesktop();
347 temp_policy
->Release();
350 // Launches the app in the sandbox and share a handle with it. The app should
351 // be able to use the handle.
352 TEST(PolicyTargetTest
, ShareHandleTest
) {
353 // The way we share handles via STARTUPINFOEX does not work on XP.
354 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
357 BrokerServices
* broker
= GetBroker();
358 ASSERT_TRUE(broker
!= NULL
);
360 base::StringPiece contents
= "Hello World";
361 std::string name
= "TestSharedMemory";
362 base::SharedMemoryCreateOptions options
;
363 options
.size
= contents
.size();
364 options
.share_read_only
= true;
365 options
.name_deprecated
= &name
;
366 base::SharedMemory writable_shmem
;
367 ASSERT_TRUE(writable_shmem
.Create(options
));
368 ASSERT_TRUE(writable_shmem
.Map(options
.size
));
369 memcpy(writable_shmem
.memory(), contents
.data(), contents
.size());
371 base::SharedMemory read_only_view
;
372 ASSERT_TRUE(read_only_view
.Open(name
, true));
374 // Get the path to the sandboxed app.
375 wchar_t prog_name
[MAX_PATH
];
376 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
378 TargetPolicy
* policy
= broker
->CreatePolicy();
379 void* shared_handle
= policy
->AddHandleToShare(
380 read_only_view
.handle());
382 base::string16
arguments(L
"\"");
383 arguments
+= prog_name
;
384 arguments
+= L
"\" -child 0 shared_memory_handle ";
385 arguments
+= base::UintToString16(
386 reinterpret_cast<unsigned int>(shared_handle
));
389 ResultCode result
= SBOX_ALL_OK
;
390 base::win::ScopedProcessInformation target
;
392 policy
->SetTokenLevel(USER_INTERACTIVE
, USER_LOCKDOWN
);
393 PROCESS_INFORMATION temp_process_info
= {};
394 result
= broker
->SpawnTarget(prog_name
, arguments
.c_str(), policy
,
398 EXPECT_EQ(SBOX_ALL_OK
, result
);
399 if (result
== SBOX_ALL_OK
)
400 target
.Set(temp_process_info
);
402 EXPECT_EQ(1, ::ResumeThread(target
.thread_handle()));
404 EXPECT_EQ(WAIT_TIMEOUT
,
405 ::WaitForSingleObject(target
.process_handle(), 2000));
407 EXPECT_TRUE(::TerminateProcess(target
.process_handle(), 0));
409 ::WaitForSingleObject(target
.process_handle(), INFINITE
);
412 } // namespace sandbox