• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
PolicyTargetTest_token(int argc,wchar_t ** argv)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.
PolicyTargetTest_steal(int argc,wchar_t ** argv)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.
PolicyTargetTest_token2(int argc,wchar_t ** argv)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.
PolicyTargetTest_token3(int argc,wchar_t ** argv)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.
PolicyTargetTest_thread(int argc,wchar_t ** argv)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.
PolicyTargetTest_thread_main(void * param)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.
PolicyTargetTest_thread2(int argc,wchar_t ** argv)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.
PolicyTargetTest_process(int argc,wchar_t ** argv)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;
162 }
163 
TEST(PolicyTargetTest,SetInformationThread)164 TEST(PolicyTargetTest, SetInformationThread) {
165   TestRunner runner;
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"));
169   }
170 
171   runner.SetTestState(AFTER_REVERT);
172   EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token"));
173 
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"));
177 }
178 
TEST(PolicyTargetTest,OpenThreadToken)179 TEST(PolicyTargetTest, OpenThreadToken) {
180   TestRunner runner;
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"));
184   }
185 
186   runner.SetTestState(AFTER_REVERT);
187   EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2"));
188 }
189 
TEST(PolicyTargetTest,OpenThreadTokenEx)190 TEST(PolicyTargetTest, OpenThreadTokenEx) {
191   TestRunner runner;
192   if (base::win::GetVersion() < base::win::VERSION_XP)
193     return;
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 
TEST(PolicyTargetTest,OpenThread)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 
TEST(PolicyTargetTest,OpenProcess)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.
TEST(PolicyTargetTest,DesktopPolicy)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(1, ::ResumeThread(target.thread_handle()));
257 
258   EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
259 
260   EXPECT_NE(::GetThreadDesktop(target.thread_id()),
261             ::GetThreadDesktop(::GetCurrentThreadId()));
262 
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));
267 
268   ::WaitForSingleObject(target.process_handle(), INFINITE);
269 
270   // Close the desktop handle.
271   temp_policy = broker->CreatePolicy();
272   temp_policy->DestroyAlternateDesktop();
273   temp_policy->Release();
274 
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);
278 }
279 
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
283 // current desktop.
TEST(PolicyTargetTest,WinstaPolicy)284 TEST(PolicyTargetTest, WinstaPolicy) {
285   BrokerServices* broker = GetBroker();
286 
287   // Precreate the desktop.
288   TargetPolicy* temp_policy = broker->CreatePolicy();
289   temp_policy->CreateAlternateDesktop(true);
290   temp_policy->Release();
291 
292   ASSERT_TRUE(broker != NULL);
293 
294   // Get the path to the sandboxed app.
295   wchar_t prog_name[MAX_PATH];
296   GetModuleFileNameW(NULL, prog_name, MAX_PATH);
297 
298   base::string16 arguments(L"\"");
299   arguments += prog_name;
300   arguments += L"\" -child 0 wait";  // Don't care about the "state" argument.
301 
302   // Launch the app.
303   ResultCode result = SBOX_ALL_OK;
304   base::win::ScopedProcessInformation target;
305 
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,
311                                &temp_process_info);
312   base::string16 desktop_name = policy->GetAlternateDesktop();
313   policy->Release();
314 
315   EXPECT_EQ(SBOX_ALL_OK, result);
316   if (result == SBOX_ALL_OK)
317     target.Set(temp_process_info);
318 
319   EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
320 
321   EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
322 
323   EXPECT_NE(::GetThreadDesktop(target.thread_id()),
324             ::GetThreadDesktop(::GetCurrentThreadId()));
325 
326   ASSERT_FALSE(desktop_name.empty());
327 
328   // Make sure there is a backslash, for the window station name.
329   EXPECT_NE(desktop_name.find_first_of(L'\\'), base::string16::npos);
330 
331   // Isolate the desktop name.
332   desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1);
333 
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));
338 
339   ::WaitForSingleObject(target.process_handle(), INFINITE);
340 
341   // Close the desktop handle.
342   temp_policy = broker->CreatePolicy();
343   temp_policy->DestroyAlternateDesktop();
344   temp_policy->Release();
345 }
346 
347 }  // namespace sandbox
348