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