|
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 <memory> |
|
6 #include <string> |
|
7 |
|
8 #include "base/strings/string16.h" |
|
9 #include "base/strings/sys_string_conversions.h" |
|
10 #include "base/win/scoped_handle.h" |
|
11 #include "base/win/scoped_process_information.h" |
|
12 #include "base/win/windows_version.h" |
|
13 #include "sandbox/win/src/sandbox.h" |
|
14 #include "sandbox/win/src/sandbox_factory.h" |
|
15 #include "sandbox/win/src/sandbox_policy.h" |
|
16 #include "sandbox/win/tests/common/controller.h" |
|
17 #include "testing/gtest/include/gtest/gtest.h" |
|
18 |
|
19 namespace { |
|
20 |
|
21 // While the shell API provides better calls than this home brew function |
|
22 // we use GetSystemWindowsDirectoryW which does not query the registry so |
|
23 // it is safe to use after revert. |
|
24 string16 MakeFullPathToSystem32(const wchar_t* name) { |
|
25 wchar_t windows_path[MAX_PATH] = {0}; |
|
26 ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH); |
|
27 string16 full_path(windows_path); |
|
28 if (full_path.empty()) { |
|
29 return full_path; |
|
30 } |
|
31 full_path += L"\\system32\\"; |
|
32 full_path += name; |
|
33 return full_path; |
|
34 } |
|
35 |
|
36 // Creates a process with the |exe| and |command| parameter using the |
|
37 // unicode and ascii version of the api. |
|
38 sandbox::SboxTestResult CreateProcessHelper(const string16& exe, |
|
39 const string16& command) { |
|
40 base::win::ScopedProcessInformation pi; |
|
41 STARTUPINFOW si = {sizeof(si)}; |
|
42 |
|
43 const wchar_t *exe_name = NULL; |
|
44 if (!exe.empty()) |
|
45 exe_name = exe.c_str(); |
|
46 |
|
47 const wchar_t *cmd_line = NULL; |
|
48 if (!command.empty()) |
|
49 cmd_line = command.c_str(); |
|
50 |
|
51 // Create the process with the unicode version of the API. |
|
52 sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED; |
|
53 if (!::CreateProcessW(exe_name, const_cast<wchar_t*>(cmd_line), NULL, NULL, |
|
54 FALSE, 0, NULL, NULL, &si, pi.Receive())) { |
|
55 DWORD last_error = GetLastError(); |
|
56 if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || |
|
57 (ERROR_ACCESS_DENIED == last_error) || |
|
58 (ERROR_FILE_NOT_FOUND == last_error)) { |
|
59 ret1 = sandbox::SBOX_TEST_DENIED; |
|
60 } else { |
|
61 ret1 = sandbox::SBOX_TEST_FAILED; |
|
62 } |
|
63 } else { |
|
64 ret1 = sandbox::SBOX_TEST_SUCCEEDED; |
|
65 } |
|
66 |
|
67 pi.Close(); |
|
68 |
|
69 // Do the same with the ansi version of the api |
|
70 STARTUPINFOA sia = {sizeof(sia)}; |
|
71 sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED; |
|
72 |
|
73 std::string narrow_cmd_line; |
|
74 if (cmd_line) |
|
75 narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8); |
|
76 if (!::CreateProcessA( |
|
77 exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL, |
|
78 cmd_line ? const_cast<char*>(narrow_cmd_line.c_str()) : NULL, |
|
79 NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) { |
|
80 DWORD last_error = GetLastError(); |
|
81 if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || |
|
82 (ERROR_ACCESS_DENIED == last_error) || |
|
83 (ERROR_FILE_NOT_FOUND == last_error)) { |
|
84 ret2 = sandbox::SBOX_TEST_DENIED; |
|
85 } else { |
|
86 ret2 = sandbox::SBOX_TEST_FAILED; |
|
87 } |
|
88 } else { |
|
89 ret2 = sandbox::SBOX_TEST_SUCCEEDED; |
|
90 } |
|
91 |
|
92 if (ret1 == ret2) |
|
93 return ret1; |
|
94 |
|
95 return sandbox::SBOX_TEST_FAILED; |
|
96 } |
|
97 |
|
98 } // namespace |
|
99 |
|
100 namespace sandbox { |
|
101 |
|
102 SBOX_TESTS_COMMAND int Process_RunApp1(int argc, wchar_t **argv) { |
|
103 if (argc != 1) { |
|
104 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
105 } |
|
106 if ((NULL == argv) || (NULL == argv[0])) { |
|
107 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
108 } |
|
109 string16 path = MakeFullPathToSystem32(argv[0]); |
|
110 |
|
111 // TEST 1: Try with the path in the app_name. |
|
112 return CreateProcessHelper(path, string16()); |
|
113 } |
|
114 |
|
115 SBOX_TESTS_COMMAND int Process_RunApp2(int argc, wchar_t **argv) { |
|
116 if (argc != 1) { |
|
117 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
118 } |
|
119 if ((NULL == argv) || (NULL == argv[0])) { |
|
120 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
121 } |
|
122 string16 path = MakeFullPathToSystem32(argv[0]); |
|
123 |
|
124 // TEST 2: Try with the path in the cmd_line. |
|
125 string16 cmd_line = L"\""; |
|
126 cmd_line += path; |
|
127 cmd_line += L"\""; |
|
128 return CreateProcessHelper(string16(), cmd_line); |
|
129 } |
|
130 |
|
131 SBOX_TESTS_COMMAND int Process_RunApp3(int argc, wchar_t **argv) { |
|
132 if (argc != 1) { |
|
133 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
134 } |
|
135 if ((NULL == argv) || (NULL == argv[0])) { |
|
136 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
137 } |
|
138 |
|
139 // TEST 3: Try file name in the cmd_line. |
|
140 return CreateProcessHelper(string16(), argv[0]); |
|
141 } |
|
142 |
|
143 SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t **argv) { |
|
144 if (argc != 1) { |
|
145 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
146 } |
|
147 if ((NULL == argv) || (NULL == argv[0])) { |
|
148 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
149 } |
|
150 |
|
151 // TEST 4: Try file name in the app_name and current directory sets correctly. |
|
152 string16 system32 = MakeFullPathToSystem32(L""); |
|
153 wchar_t current_directory[MAX_PATH + 1]; |
|
154 int result4; |
|
155 bool test_succeeded = false; |
|
156 DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); |
|
157 if (!ret) |
|
158 return SBOX_TEST_FIRST_ERROR; |
|
159 |
|
160 if (ret < MAX_PATH) { |
|
161 current_directory[ret] = L'\\'; |
|
162 current_directory[ret+1] = L'\0'; |
|
163 if (::SetCurrentDirectory(system32.c_str())) { |
|
164 result4 = CreateProcessHelper(argv[0], string16()); |
|
165 if (::SetCurrentDirectory(current_directory)) { |
|
166 test_succeeded = true; |
|
167 } |
|
168 } else { |
|
169 return SBOX_TEST_SECOND_ERROR; |
|
170 } |
|
171 } |
|
172 if (!test_succeeded) |
|
173 result4 = SBOX_TEST_FAILED; |
|
174 |
|
175 return result4; |
|
176 } |
|
177 |
|
178 SBOX_TESTS_COMMAND int Process_RunApp5(int argc, wchar_t **argv) { |
|
179 if (argc != 1) { |
|
180 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
181 } |
|
182 if ((NULL == argv) || (NULL == argv[0])) { |
|
183 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
184 } |
|
185 string16 path = MakeFullPathToSystem32(argv[0]); |
|
186 |
|
187 // TEST 5: Try with the path in the cmd_line and arguments. |
|
188 string16 cmd_line = L"\""; |
|
189 cmd_line += path; |
|
190 cmd_line += L"\" /I"; |
|
191 return CreateProcessHelper(string16(), cmd_line); |
|
192 } |
|
193 |
|
194 SBOX_TESTS_COMMAND int Process_RunApp6(int argc, wchar_t **argv) { |
|
195 if (argc != 1) { |
|
196 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
197 } |
|
198 if ((NULL == argv) || (NULL == argv[0])) { |
|
199 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
200 } |
|
201 |
|
202 // TEST 6: Try with the file_name in the cmd_line and arguments. |
|
203 string16 cmd_line = argv[0]; |
|
204 cmd_line += L" /I"; |
|
205 return CreateProcessHelper(string16(), cmd_line); |
|
206 } |
|
207 |
|
208 // Creates a process and checks if it's possible to get a handle to it's token. |
|
209 SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) { |
|
210 if (argc != 1) |
|
211 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
212 |
|
213 if ((NULL == argv) || (NULL == argv[0])) |
|
214 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
|
215 |
|
216 string16 path = MakeFullPathToSystem32(argv[0]); |
|
217 |
|
218 base::win::ScopedProcessInformation pi; |
|
219 STARTUPINFOW si = {sizeof(si)}; |
|
220 |
|
221 if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, |
|
222 NULL, NULL, &si, pi.Receive())) { |
|
223 return SBOX_TEST_FAILED; |
|
224 } |
|
225 |
|
226 HANDLE token = NULL; |
|
227 BOOL result = |
|
228 ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token); |
|
229 DWORD error = ::GetLastError(); |
|
230 |
|
231 base::win::ScopedHandle token_handle(token); |
|
232 |
|
233 if (!::TerminateProcess(pi.process_handle(), 0)) |
|
234 return SBOX_TEST_FAILED; |
|
235 |
|
236 if (result && token) |
|
237 return SBOX_TEST_SUCCEEDED; |
|
238 |
|
239 if (ERROR_ACCESS_DENIED == error) |
|
240 return SBOX_TEST_DENIED; |
|
241 |
|
242 return SBOX_TEST_FAILED; |
|
243 } |
|
244 |
|
245 |
|
246 SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t **argv) { |
|
247 HANDLE token; |
|
248 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) { |
|
249 if (ERROR_ACCESS_DENIED == ::GetLastError()) { |
|
250 return SBOX_TEST_DENIED; |
|
251 } |
|
252 } else { |
|
253 ::CloseHandle(token); |
|
254 return SBOX_TEST_SUCCEEDED; |
|
255 } |
|
256 |
|
257 return SBOX_TEST_FAILED; |
|
258 } |
|
259 |
|
260 TEST(ProcessPolicyTest, TestAllAccess) { |
|
261 // Check if the "all access" rule fails to be added when the token is too |
|
262 // powerful. |
|
263 TestRunner runner; |
|
264 |
|
265 // Check the failing case. |
|
266 runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); |
|
267 EXPECT_EQ(SBOX_ERROR_UNSUPPORTED, |
|
268 runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, |
|
269 TargetPolicy::PROCESS_ALL_EXEC, |
|
270 L"this is not important")); |
|
271 |
|
272 // Check the working case. |
|
273 runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE); |
|
274 |
|
275 EXPECT_EQ(SBOX_ALL_OK, |
|
276 runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, |
|
277 TargetPolicy::PROCESS_ALL_EXEC, |
|
278 L"this is not important")); |
|
279 } |
|
280 |
|
281 TEST(ProcessPolicyTest, CreateProcessAW) { |
|
282 TestRunner runner; |
|
283 string16 exe_path = MakeFullPathToSystem32(L"findstr.exe"); |
|
284 string16 system32 = MakeFullPathToSystem32(L""); |
|
285 ASSERT_TRUE(!exe_path.empty()); |
|
286 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, |
|
287 TargetPolicy::PROCESS_MIN_EXEC, |
|
288 exe_path.c_str())); |
|
289 |
|
290 // Need to add directory rules for the directories that we use in |
|
291 // SetCurrentDirectory. |
|
292 EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, |
|
293 system32.c_str())); |
|
294 |
|
295 wchar_t current_directory[MAX_PATH]; |
|
296 DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); |
|
297 ASSERT_TRUE(0 != ret && ret < MAX_PATH); |
|
298 |
|
299 wcscat_s(current_directory, MAX_PATH, L"\\"); |
|
300 EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, |
|
301 current_directory)); |
|
302 |
|
303 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp1 calc.exe")); |
|
304 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp2 calc.exe")); |
|
305 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp3 calc.exe")); |
|
306 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp5 calc.exe")); |
|
307 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp6 calc.exe")); |
|
308 |
|
309 EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
|
310 runner.RunTest(L"Process_RunApp1 findstr.exe")); |
|
311 EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
|
312 runner.RunTest(L"Process_RunApp2 findstr.exe")); |
|
313 EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
|
314 runner.RunTest(L"Process_RunApp3 findstr.exe")); |
|
315 EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
|
316 runner.RunTest(L"Process_RunApp5 findstr.exe")); |
|
317 EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
|
318 runner.RunTest(L"Process_RunApp6 findstr.exe")); |
|
319 |
|
320 #if !defined(_WIN64) |
|
321 if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) { |
|
322 // WinXP results are not reliable. |
|
323 EXPECT_EQ(SBOX_TEST_SECOND_ERROR, |
|
324 runner.RunTest(L"Process_RunApp4 calc.exe")); |
|
325 EXPECT_EQ(SBOX_TEST_SECOND_ERROR, |
|
326 runner.RunTest(L"Process_RunApp4 findstr.exe")); |
|
327 } |
|
328 #endif |
|
329 } |
|
330 |
|
331 TEST(ProcessPolicyTest, OpenToken) { |
|
332 TestRunner runner; |
|
333 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken")); |
|
334 } |
|
335 |
|
336 TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) { |
|
337 TestRunner runner; |
|
338 string16 exe_path = MakeFullPathToSystem32(L"findstr.exe"); |
|
339 ASSERT_TRUE(!exe_path.empty()); |
|
340 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, |
|
341 TargetPolicy::PROCESS_MIN_EXEC, |
|
342 exe_path.c_str())); |
|
343 |
|
344 EXPECT_EQ(SBOX_TEST_DENIED, |
|
345 runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); |
|
346 } |
|
347 |
|
348 TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) { |
|
349 TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE); |
|
350 string16 exe_path = MakeFullPathToSystem32(L"findstr.exe"); |
|
351 ASSERT_TRUE(!exe_path.empty()); |
|
352 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, |
|
353 TargetPolicy::PROCESS_ALL_EXEC, |
|
354 exe_path.c_str())); |
|
355 |
|
356 EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
|
357 runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); |
|
358 } |
|
359 |
|
360 TEST(ProcessPolicyTest, TestGetProcessTokenMinAccessNoJob) { |
|
361 TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN); |
|
362 string16 exe_path = MakeFullPathToSystem32(L"findstr.exe"); |
|
363 ASSERT_TRUE(!exe_path.empty()); |
|
364 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, |
|
365 TargetPolicy::PROCESS_MIN_EXEC, |
|
366 exe_path.c_str())); |
|
367 |
|
368 EXPECT_EQ(SBOX_TEST_DENIED, |
|
369 runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); |
|
370 } |
|
371 |
|
372 TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccessNoJob) { |
|
373 TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE); |
|
374 string16 exe_path = MakeFullPathToSystem32(L"findstr.exe"); |
|
375 ASSERT_TRUE(!exe_path.empty()); |
|
376 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, |
|
377 TargetPolicy::PROCESS_ALL_EXEC, |
|
378 exe_path.c_str())); |
|
379 |
|
380 EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
|
381 runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); |
|
382 } |
|
383 |
|
384 } // namespace sandbox |