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 <memory> | |
6 #include <string> | |
7 | |
8 #include "base/sys_string_conversions.h" | |
9 #include "base/win/scoped_handle.h" | |
10 #include "base/win/scoped_process_information.h" | |
11 #include "sandbox/src/sandbox.h" | |
12 #include "sandbox/src/sandbox_policy.h" | |
13 #include "sandbox/src/sandbox_factory.h" | |
14 #include "sandbox/tests/common/controller.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 | |
17 namespace { | |
18 | |
19 // While the shell API provides better calls than this home brew function | |
20 // we use GetSystemWindowsDirectoryW which does not query the registry so | |
21 // it is safe to use after revert. | |
22 std::wstring MakeFullPathToSystem32(const wchar_t* name) { | |
23 wchar_t windows_path[MAX_PATH] = {0}; | |
24 ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH); | |
25 std::wstring full_path(windows_path); | |
26 if (full_path.empty()) { | |
27 return full_path; | |
28 } | |
29 full_path += L"\\system32\\"; | |
30 full_path += name; | |
31 return full_path; | |
32 } | |
33 | |
34 // Creates a process with the |exe| and |command| parameter using the | |
35 // unicode and ascii version of the api. | |
36 sandbox::SboxTestResult CreateProcessHelper(const std::wstring &exe, | |
37 const std::wstring &command) { | |
38 base::win::ScopedProcessInformation pi; | |
39 STARTUPINFOW si = {sizeof(si)}; | |
40 | |
41 const wchar_t *exe_name = NULL; | |
42 if (!exe.empty()) | |
43 exe_name = exe.c_str(); | |
44 | |
45 const wchar_t *cmd_line = NULL; | |
46 if (!command.empty()) | |
47 cmd_line = command.c_str(); | |
48 | |
49 // Create the process with the unicode version of the API. | |
50 sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED; | |
51 if (!::CreateProcessW(exe_name, const_cast<wchar_t*>(cmd_line), NULL, NULL, | |
52 FALSE, 0, NULL, NULL, &si, pi.Receive())) { | |
53 DWORD last_error = GetLastError(); | |
54 if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || | |
55 (ERROR_ACCESS_DENIED == last_error) || | |
56 (ERROR_FILE_NOT_FOUND == last_error)) { | |
57 ret1 = sandbox::SBOX_TEST_DENIED; | |
58 } else { | |
59 ret1 = sandbox::SBOX_TEST_FAILED; | |
60 } | |
61 } else { | |
62 ret1 = sandbox::SBOX_TEST_SUCCEEDED; | |
63 } | |
64 | |
65 pi.Close(); | |
66 | |
67 // Do the same with the ansi version of the api | |
68 STARTUPINFOA sia = {sizeof(sia)}; | |
69 sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED; | |
70 | |
71 std::string narrow_cmd_line; | |
72 if (cmd_line) | |
73 narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8); | |
74 if (!::CreateProcessA( | |
75 exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL, | |
76 cmd_line ? const_cast<char*>(narrow_cmd_line.c_str()) : NULL, | |
77 NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) { | |
78 DWORD last_error = GetLastError(); | |
79 if ((ERROR_NOT_ENOUGH_QUOTA == last_error) || | |
80 (ERROR_ACCESS_DENIED == last_error) || | |
81 (ERROR_FILE_NOT_FOUND == last_error)) { | |
82 ret2 = sandbox::SBOX_TEST_DENIED; | |
83 } else { | |
84 ret2 = sandbox::SBOX_TEST_FAILED; | |
85 } | |
86 } else { | |
87 ret2 = sandbox::SBOX_TEST_SUCCEEDED; | |
88 } | |
89 | |
90 if (ret1 == ret2) | |
91 return ret1; | |
92 | |
93 return sandbox::SBOX_TEST_FAILED; | |
94 } | |
95 | |
96 } // namespace | |
97 | |
98 namespace sandbox { | |
99 | |
100 // Tries to create the process in argv[0] using 7 different ways. | |
101 // Since we also try the Ansi and Unicode version of the CreateProcess API, | |
102 // The process referenced by argv[0] will be spawned 14 times. | |
103 SBOX_TESTS_COMMAND int Process_RunApp(int argc, wchar_t **argv) { | |
104 if (argc != 1) { | |
105 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | |
106 } | |
107 if ((NULL == argv) || (NULL == argv[0])) { | |
108 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | |
109 } | |
110 std::wstring path = MakeFullPathToSystem32(argv[0]); | |
111 | |
112 // TEST 1: Try with the path in the app_name. | |
113 int result1 = CreateProcessHelper(path, std::wstring()); | |
114 | |
115 // TEST 2: Try with the path in the cmd_line. | |
116 std::wstring cmd_line = L"\""; | |
117 cmd_line += path; | |
118 cmd_line += L"\""; | |
119 int result2 = CreateProcessHelper(std::wstring(), cmd_line); | |
120 | |
121 // TEST 3: Try file name in the cmd_line. | |
122 int result3 = CreateProcessHelper(std::wstring(), argv[0]); | |
123 | |
124 // TEST 4: Try file name in the app_name and current directory sets correctly. | |
125 std::wstring system32 = MakeFullPathToSystem32(L""); | |
126 wchar_t current_directory[MAX_PATH + 1]; | |
127 int result4; | |
128 bool test_succeeded = false; | |
129 DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); | |
130 if (0 != ret && ret < MAX_PATH) { | |
131 current_directory[ret] = L'\\'; | |
132 current_directory[ret+1] = L'\0'; | |
133 if (::SetCurrentDirectory(system32.c_str())) { | |
134 result4 = CreateProcessHelper(argv[0], std::wstring()); | |
135 if (::SetCurrentDirectory(current_directory)) { | |
136 test_succeeded = true; | |
137 } | |
138 } | |
139 } | |
140 if (!test_succeeded) | |
141 result4 = SBOX_TEST_FAILED; | |
142 | |
143 // TEST 5: Try with the path in the cmd_line and arguments. | |
144 cmd_line = L"\""; | |
145 cmd_line += path; | |
146 cmd_line += L"\" /INSERT"; | |
147 int result5 = CreateProcessHelper(std::wstring(), cmd_line); | |
148 | |
149 // TEST 6: Try with the file_name in the cmd_line and arguments. | |
150 cmd_line = argv[0]; | |
151 cmd_line += L" /INSERT"; | |
152 int result6 = CreateProcessHelper(std::wstring(), cmd_line); | |
153 | |
154 // TEST 7: Try with the path without the drive. | |
155 cmd_line = path.substr(path.find(L'\\')); | |
156 int result7 = CreateProcessHelper(std::wstring(), cmd_line); | |
157 | |
158 // Check if they all returned the same thing. | |
159 if ((result1 == result2) && (result2 == result3) && (result3 == result4) && | |
160 (result4 == result5) && (result5 == result6) && (result6 == result7)) | |
161 return result1; | |
162 | |
163 return SBOX_TEST_FAILED; | |
164 } | |
165 | |
166 // Creates a process and checks if it's possible to get a handle to it's token. | |
167 SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) { | |
168 if (argc != 1) | |
169 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | |
170 | |
171 if ((NULL == argv) || (NULL == argv[0])) | |
172 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | |
173 | |
174 std::wstring path = MakeFullPathToSystem32(argv[0]); | |
175 | |
176 base::win::ScopedProcessInformation pi; | |
177 STARTUPINFOW si = {sizeof(si)}; | |
178 | |
179 if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, | |
180 NULL, NULL, &si, pi.Receive())) { | |
181 return SBOX_TEST_FAILED; | |
182 } | |
183 | |
184 HANDLE token = NULL; | |
185 BOOL result = | |
186 ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token); | |
187 DWORD error = ::GetLastError(); | |
188 | |
189 base::win::ScopedHandle token_handle(token); | |
190 | |
191 if (!::TerminateProcess(pi.process_handle(), 0)) | |
192 return SBOX_TEST_FAILED; | |
193 | |
194 if (result && token) | |
195 return SBOX_TEST_SUCCEEDED; | |
196 | |
197 if (ERROR_ACCESS_DENIED == error) | |
198 return SBOX_TEST_DENIED; | |
199 | |
200 return SBOX_TEST_FAILED; | |
201 } | |
202 | |
203 | |
204 SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t **argv) { | |
205 HANDLE token; | |
206 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) { | |
207 if (ERROR_ACCESS_DENIED == ::GetLastError()) { | |
208 return SBOX_TEST_DENIED; | |
209 } | |
210 } else { | |
211 ::CloseHandle(token); | |
212 return SBOX_TEST_SUCCEEDED; | |
213 } | |
214 | |
215 return SBOX_TEST_FAILED; | |
216 } | |
217 | |
218 TEST(ProcessPolicyTest, TestAllAccess) { | |
219 // Check if the "all access" rule fails to be added when the token is too | |
220 // powerful. | |
221 TestRunner runner; | |
222 | |
223 // Check the failing case. | |
224 runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN); | |
225 EXPECT_EQ(SBOX_ERROR_UNSUPPORTED, | |
226 runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, | |
227 TargetPolicy::PROCESS_ALL_EXEC, | |
228 L"this is not important")); | |
229 | |
230 // Check the working case. | |
231 runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE); | |
232 | |
233 EXPECT_EQ(SBOX_ALL_OK, | |
234 runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS, | |
235 TargetPolicy::PROCESS_ALL_EXEC, | |
236 L"this is not important")); | |
237 } | |
238 | |
239 // This test is disabled. See bug 1305476. | |
240 TEST(ProcessPolicyTest, DISABLED_RunFindstrExe) { | |
241 TestRunner runner; | |
242 std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); | |
243 std::wstring system32 = MakeFullPathToSystem32(L""); | |
244 ASSERT_TRUE(!exe_path.empty()); | |
245 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, | |
246 TargetPolicy::PROCESS_MIN_EXEC, | |
247 exe_path.c_str())); | |
248 | |
249 // Need to add directory rules for the directories that we use in | |
250 // SetCurrentDirectory. | |
251 EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, | |
252 system32.c_str())); | |
253 | |
254 wchar_t current_directory[MAX_PATH]; | |
255 DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory); | |
256 ASSERT_TRUE(0 != ret && ret < MAX_PATH); | |
257 | |
258 wcscat_s(current_directory, MAX_PATH, L"\\"); | |
259 EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, | |
260 current_directory)); | |
261 | |
262 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_RunApp findstr.exe")); | |
263 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp calc.exe")); | |
264 } | |
265 | |
266 TEST(ProcessPolicyTest, OpenToken) { | |
267 TestRunner runner; | |
268 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken")); | |
269 } | |
270 | |
271 TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) { | |
272 TestRunner runner; | |
273 std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); | |
274 ASSERT_TRUE(!exe_path.empty()); | |
275 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, | |
276 TargetPolicy::PROCESS_MIN_EXEC, | |
277 exe_path.c_str())); | |
278 | |
279 EXPECT_EQ(SBOX_TEST_DENIED, | |
280 runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); | |
281 } | |
282 | |
283 TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) { | |
284 TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE); | |
285 std::wstring exe_path = MakeFullPathToSystem32(L"findstr.exe"); | |
286 ASSERT_TRUE(!exe_path.empty()); | |
287 EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS, | |
288 TargetPolicy::PROCESS_ALL_EXEC, | |
289 exe_path.c_str())); | |
290 | |
291 EXPECT_EQ(SBOX_TEST_SUCCEEDED, | |
292 runner.RunTest(L"Process_GetChildProcessToken findstr.exe")); | |
293 } | |
294 | |
295 } // namespace sandbox | |
OLD | NEW |