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