OLD | NEW |
| (Empty) |
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/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" | |
16 | |
17 #if defined(OS_WIN) | |
18 #include "base/win/win_util.h" | |
19 #endif | |
20 | |
21 namespace sandbox { | |
22 | |
23 #define BINDNTDLL(name) \ | |
24 name ## Function name = reinterpret_cast<name ## Function>( \ | |
25 ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) | |
26 | |
27 // Reverts to self and verify that SetInformationToken was faked. Returns | |
28 // SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked. | |
29 SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t **argv) { | |
30 HANDLE thread_token; | |
31 // Get the thread token, using impersonation. | |
32 if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | | |
33 TOKEN_DUPLICATE, FALSE, &thread_token)) | |
34 return ::GetLastError(); | |
35 | |
36 ::RevertToSelf(); | |
37 ::CloseHandle(thread_token); | |
38 | |
39 int ret = SBOX_TEST_FAILED; | |
40 if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, | |
41 FALSE, &thread_token)) { | |
42 ret = SBOX_TEST_SUCCEEDED; | |
43 ::CloseHandle(thread_token); | |
44 } | |
45 return ret; | |
46 } | |
47 | |
48 // Stores the high privilege token on a static variable, change impersonation | |
49 // again to that one and verify that we are not interfering anymore with | |
50 // RevertToSelf. | |
51 SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t **argv) { | |
52 static HANDLE thread_token; | |
53 if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) { | |
54 if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | | |
55 TOKEN_DUPLICATE, FALSE, &thread_token)) | |
56 return ::GetLastError(); | |
57 } else { | |
58 if (!::SetThreadToken(NULL, thread_token)) | |
59 return ::GetLastError(); | |
60 | |
61 // See if we fake the call again. | |
62 int ret = PolicyTargetTest_token(argc, argv); | |
63 ::CloseHandle(thread_token); | |
64 return ret; | |
65 } | |
66 return 0; | |
67 } | |
68 | |
69 // Opens the thread token with and without impersonation. | |
70 SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t **argv) { | |
71 HANDLE thread_token; | |
72 // Get the thread token, using impersonation. | |
73 if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | | |
74 TOKEN_DUPLICATE, FALSE, &thread_token)) | |
75 return ::GetLastError(); | |
76 ::CloseHandle(thread_token); | |
77 | |
78 // Get the thread token, without impersonation. | |
79 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, | |
80 TRUE, &thread_token)) | |
81 return ::GetLastError(); | |
82 ::CloseHandle(thread_token); | |
83 return SBOX_TEST_SUCCEEDED; | |
84 } | |
85 | |
86 // Opens the thread token with and without impersonation, using | |
87 // NtOpenThreadTokenEX. | |
88 SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t **argv) { | |
89 BINDNTDLL(NtOpenThreadTokenEx); | |
90 if (!NtOpenThreadTokenEx) | |
91 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | |
92 | |
93 HANDLE thread_token; | |
94 // Get the thread token, using impersonation. | |
95 NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(), | |
96 TOKEN_IMPERSONATE | TOKEN_DUPLICATE, | |
97 FALSE, 0, &thread_token); | |
98 if (status == STATUS_NO_TOKEN) | |
99 return ERROR_NO_TOKEN; | |
100 if (!NT_SUCCESS(status)) | |
101 return SBOX_TEST_FAILED; | |
102 | |
103 ::CloseHandle(thread_token); | |
104 | |
105 // Get the thread token, without impersonation. | |
106 status = NtOpenThreadTokenEx(GetCurrentThread(), | |
107 TOKEN_IMPERSONATE | TOKEN_DUPLICATE, TRUE, 0, | |
108 &thread_token); | |
109 if (!NT_SUCCESS(status)) | |
110 return SBOX_TEST_FAILED; | |
111 | |
112 ::CloseHandle(thread_token); | |
113 return SBOX_TEST_SUCCEEDED; | |
114 } | |
115 | |
116 // Tests that we can open the current thread. | |
117 SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t **argv) { | |
118 DWORD thread_id = ::GetCurrentThreadId(); | |
119 HANDLE thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id); | |
120 if (!thread) | |
121 return ::GetLastError(); | |
122 if (!::CloseHandle(thread)) | |
123 return ::GetLastError(); | |
124 | |
125 return SBOX_TEST_SUCCEEDED; | |
126 } | |
127 | |
128 // New thread entry point: do nothing. | |
129 DWORD WINAPI PolicyTargetTest_thread_main(void* param) { | |
130 ::Sleep(INFINITE); | |
131 return 0; | |
132 } | |
133 | |
134 // Tests that we can create a new thread, and open it. | |
135 SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t **argv) { | |
136 // Use default values to create a new thread. | |
137 DWORD thread_id; | |
138 HANDLE thread = ::CreateThread(NULL, 0, &PolicyTargetTest_thread_main, 0, 0, | |
139 &thread_id); | |
140 if (!thread) | |
141 return ::GetLastError(); | |
142 if (!::CloseHandle(thread)) | |
143 return ::GetLastError(); | |
144 | |
145 thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id); | |
146 if (!thread) | |
147 return ::GetLastError(); | |
148 | |
149 if (!::CloseHandle(thread)) | |
150 return ::GetLastError(); | |
151 | |
152 return SBOX_TEST_SUCCEEDED; | |
153 } | |
154 | |
155 // Tests that we can call CreateProcess. | |
156 SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t **argv) { | |
157 // Use default values to create a new process. | |
158 STARTUPINFO startup_info = {0}; | |
159 startup_info.cb = sizeof(startup_info); | |
160 PROCESS_INFORMATION temp_process_info = {}; | |
161 // Note: CreateProcessW() can write to its lpCommandLine, don't pass a | |
162 // raw string literal. | |
163 base::string16 writable_cmdline_str(L"foo.exe"); | |
164 if (!::CreateProcessW(L"foo.exe", &writable_cmdline_str[0], NULL, NULL, FALSE, | |
165 0, NULL, NULL, &startup_info, &temp_process_info)) | |
166 return SBOX_TEST_SUCCEEDED; | |
167 base::win::ScopedProcessInformation process_info(temp_process_info); | |
168 return SBOX_TEST_FAILED; | |
169 } | |
170 | |
171 TEST(PolicyTargetTest, SetInformationThread) { | |
172 TestRunner runner; | |
173 runner.SetTestState(BEFORE_REVERT); | |
174 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token")); | |
175 | |
176 runner.SetTestState(AFTER_REVERT); | |
177 EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token")); | |
178 | |
179 runner.SetTestState(EVERY_STATE); | |
180 EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal")); | |
181 } | |
182 | |
183 TEST(PolicyTargetTest, OpenThreadToken) { | |
184 TestRunner runner; | |
185 runner.SetTestState(BEFORE_REVERT); | |
186 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2")); | |
187 | |
188 runner.SetTestState(AFTER_REVERT); | |
189 EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2")); | |
190 } | |
191 | |
192 TEST(PolicyTargetTest, OpenThreadTokenEx) { | |
193 TestRunner runner; | |
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(1u, ::ResumeThread(target.thread_handle())); | |
257 | |
258 EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), | |
259 ::WaitForSingleObject(target.process_handle(), 2000)); | |
260 | |
261 EXPECT_NE(::GetThreadDesktop(target.thread_id()), | |
262 ::GetThreadDesktop(::GetCurrentThreadId())); | |
263 | |
264 HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); | |
265 EXPECT_TRUE(NULL != desk); | |
266 EXPECT_TRUE(::CloseDesktop(desk)); | |
267 EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); | |
268 | |
269 ::WaitForSingleObject(target.process_handle(), INFINITE); | |
270 | |
271 // Close the desktop handle. | |
272 temp_policy = broker->CreatePolicy(); | |
273 temp_policy->DestroyAlternateDesktop(); | |
274 temp_policy->Release(); | |
275 | |
276 // Make sure the desktop does not exist anymore. | |
277 desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); | |
278 EXPECT_TRUE(NULL == desk); | |
279 } | |
280 | |
281 // Launches the app in the sandbox and ask it to wait in an | |
282 // infinite loop. Waits for 2 seconds and then check if the | |
283 // winstation associated with the app thread is not the same as the | |
284 // current desktop. | |
285 TEST(PolicyTargetTest, WinstaPolicy) { | |
286 BrokerServices* broker = GetBroker(); | |
287 | |
288 // Precreate the desktop. | |
289 TargetPolicy* temp_policy = broker->CreatePolicy(); | |
290 temp_policy->CreateAlternateDesktop(true); | |
291 temp_policy->Release(); | |
292 | |
293 ASSERT_TRUE(broker != NULL); | |
294 | |
295 // Get the path to the sandboxed app. | |
296 wchar_t prog_name[MAX_PATH]; | |
297 GetModuleFileNameW(NULL, prog_name, MAX_PATH); | |
298 | |
299 base::string16 arguments(L"\""); | |
300 arguments += prog_name; | |
301 arguments += L"\" -child 0 wait"; // Don't care about the "state" argument. | |
302 | |
303 // Launch the app. | |
304 ResultCode result = SBOX_ALL_OK; | |
305 base::win::ScopedProcessInformation target; | |
306 | |
307 TargetPolicy* policy = broker->CreatePolicy(); | |
308 policy->SetAlternateDesktop(true); | |
309 policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); | |
310 PROCESS_INFORMATION temp_process_info = {}; | |
311 result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, | |
312 &temp_process_info); | |
313 base::string16 desktop_name = policy->GetAlternateDesktop(); | |
314 policy->Release(); | |
315 | |
316 EXPECT_EQ(SBOX_ALL_OK, result); | |
317 if (result == SBOX_ALL_OK) | |
318 target.Set(temp_process_info); | |
319 | |
320 EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); | |
321 | |
322 EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), | |
323 ::WaitForSingleObject(target.process_handle(), 2000)); | |
324 | |
325 EXPECT_NE(::GetThreadDesktop(target.thread_id()), | |
326 ::GetThreadDesktop(::GetCurrentThreadId())); | |
327 | |
328 ASSERT_FALSE(desktop_name.empty()); | |
329 | |
330 // Make sure there is a backslash, for the window station name. | |
331 EXPECT_NE(desktop_name.find_first_of(L'\\'), base::string16::npos); | |
332 | |
333 // Isolate the desktop name. | |
334 desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1); | |
335 | |
336 HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE); | |
337 // This should fail if the desktop is really on another window station. | |
338 EXPECT_FALSE(NULL != desk); | |
339 EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); | |
340 | |
341 ::WaitForSingleObject(target.process_handle(), INFINITE); | |
342 | |
343 // Close the desktop handle. | |
344 temp_policy = broker->CreatePolicy(); | |
345 temp_policy->DestroyAlternateDesktop(); | |
346 temp_policy->Release(); | |
347 } | |
348 | |
349 // Launches the app in the sandbox and share a handle with it. The app should | |
350 // be able to use the handle. | |
351 TEST(PolicyTargetTest, ShareHandleTest) { | |
352 | |
353 BrokerServices* broker = GetBroker(); | |
354 ASSERT_TRUE(broker != NULL); | |
355 | |
356 base::StringPiece contents = "Hello World"; | |
357 std::string name = "TestSharedMemory"; | |
358 base::SharedMemoryCreateOptions options; | |
359 options.size = contents.size(); | |
360 options.share_read_only = true; | |
361 options.name_deprecated = &name; | |
362 base::SharedMemory writable_shmem; | |
363 ASSERT_TRUE(writable_shmem.Create(options)); | |
364 ASSERT_TRUE(writable_shmem.Map(options.size)); | |
365 memcpy(writable_shmem.memory(), contents.data(), contents.size()); | |
366 | |
367 base::SharedMemory read_only_view; | |
368 ASSERT_TRUE(read_only_view.Open(name, true)); | |
369 | |
370 // Get the path to the sandboxed app. | |
371 wchar_t prog_name[MAX_PATH]; | |
372 GetModuleFileNameW(NULL, prog_name, MAX_PATH); | |
373 | |
374 TargetPolicy* policy = broker->CreatePolicy(); | |
375 policy->AddHandleToShare(read_only_view.handle().GetHandle()); | |
376 | |
377 base::string16 arguments(L"\""); | |
378 arguments += prog_name; | |
379 arguments += L"\" -child 0 shared_memory_handle "; | |
380 arguments += base::UintToString16( | |
381 base::win::HandleToUint32(read_only_view.handle().GetHandle())); | |
382 | |
383 // Launch the app. | |
384 ResultCode result = SBOX_ALL_OK; | |
385 base::win::ScopedProcessInformation target; | |
386 | |
387 policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); | |
388 PROCESS_INFORMATION temp_process_info = {}; | |
389 result = broker->SpawnTarget(prog_name, arguments.c_str(), policy, | |
390 &temp_process_info); | |
391 policy->Release(); | |
392 | |
393 EXPECT_EQ(SBOX_ALL_OK, result); | |
394 if (result == SBOX_ALL_OK) | |
395 target.Set(temp_process_info); | |
396 | |
397 EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); | |
398 | |
399 EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), | |
400 ::WaitForSingleObject(target.process_handle(), 2000)); | |
401 | |
402 EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); | |
403 | |
404 ::WaitForSingleObject(target.process_handle(), INFINITE); | |
405 } | |
406 | |
407 } // namespace sandbox | |
OLD | NEW |