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.
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"
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 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()) {
31 full_path
+= L
"\\system32\\";
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 base::string16
& exe
,
39 const base::string16
& command
) {
40 base::win::ScopedProcessInformation pi
;
41 STARTUPINFOW si
= {sizeof(si
)};
43 const wchar_t *exe_name
= NULL
;
45 exe_name
= exe
.c_str();
47 base::string16 writable_command
= command
;
49 // Create the process with the unicode version of the API.
50 sandbox::SboxTestResult ret1
= sandbox::SBOX_TEST_FAILED
;
51 PROCESS_INFORMATION temp_process_info
= {};
52 if (::CreateProcessW(exe_name
,
53 command
.empty() ? NULL
: &writable_command
[0],
61 &temp_process_info
)) {
62 pi
.Set(temp_process_info
);
63 ret1
= sandbox::SBOX_TEST_SUCCEEDED
;
65 DWORD last_error
= GetLastError();
66 if ((ERROR_NOT_ENOUGH_QUOTA
== last_error
) ||
67 (ERROR_ACCESS_DENIED
== last_error
) ||
68 (ERROR_FILE_NOT_FOUND
== last_error
)) {
69 ret1
= sandbox::SBOX_TEST_DENIED
;
71 ret1
= sandbox::SBOX_TEST_FAILED
;
77 // Do the same with the ansi version of the api
78 STARTUPINFOA sia
= {sizeof(sia
)};
79 sandbox::SboxTestResult ret2
= sandbox::SBOX_TEST_FAILED
;
81 std::string narrow_cmd_line
=
82 base::SysWideToMultiByte(command
.c_str(), CP_UTF8
);
84 exe_name
? base::SysWideToMultiByte(exe_name
, CP_UTF8
).c_str() : NULL
,
85 command
.empty() ? NULL
: &narrow_cmd_line
[0],
86 NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &sia
, &temp_process_info
)) {
87 pi
.Set(temp_process_info
);
88 ret2
= sandbox::SBOX_TEST_SUCCEEDED
;
90 DWORD last_error
= GetLastError();
91 if ((ERROR_NOT_ENOUGH_QUOTA
== last_error
) ||
92 (ERROR_ACCESS_DENIED
== last_error
) ||
93 (ERROR_FILE_NOT_FOUND
== last_error
)) {
94 ret2
= sandbox::SBOX_TEST_DENIED
;
96 ret2
= sandbox::SBOX_TEST_FAILED
;
103 return sandbox::SBOX_TEST_FAILED
;
110 SBOX_TESTS_COMMAND
int Process_RunApp1(int argc
, wchar_t **argv
) {
112 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
114 if ((NULL
== argv
) || (NULL
== argv
[0])) {
115 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
117 base::string16 path
= MakeFullPathToSystem32(argv
[0]);
119 // TEST 1: Try with the path in the app_name.
120 return CreateProcessHelper(path
, base::string16());
123 SBOX_TESTS_COMMAND
int Process_RunApp2(int argc
, wchar_t **argv
) {
125 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
127 if ((NULL
== argv
) || (NULL
== argv
[0])) {
128 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
130 base::string16 path
= MakeFullPathToSystem32(argv
[0]);
132 // TEST 2: Try with the path in the cmd_line.
133 base::string16 cmd_line
= L
"\"";
136 return CreateProcessHelper(base::string16(), cmd_line
);
139 SBOX_TESTS_COMMAND
int Process_RunApp3(int argc
, wchar_t **argv
) {
141 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
143 if ((NULL
== argv
) || (NULL
== argv
[0])) {
144 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
147 // TEST 3: Try file name in the cmd_line.
148 return CreateProcessHelper(base::string16(), argv
[0]);
151 SBOX_TESTS_COMMAND
int Process_RunApp4(int argc
, wchar_t **argv
) {
153 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
155 if ((NULL
== argv
) || (NULL
== argv
[0])) {
156 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
159 // TEST 4: Try file name in the app_name and current directory sets correctly.
160 base::string16 system32
= MakeFullPathToSystem32(L
"");
161 wchar_t current_directory
[MAX_PATH
+ 1];
162 DWORD ret
= ::GetCurrentDirectory(MAX_PATH
, current_directory
);
164 return SBOX_TEST_FIRST_ERROR
;
166 return SBOX_TEST_FAILED
;
168 current_directory
[ret
] = L
'\\';
169 current_directory
[ret
+1] = L
'\0';
170 if (!::SetCurrentDirectory(system32
.c_str())) {
171 return SBOX_TEST_SECOND_ERROR
;
174 const int result4
= CreateProcessHelper(argv
[0], base::string16());
175 return ::SetCurrentDirectory(current_directory
) ? result4
: SBOX_TEST_FAILED
;
178 SBOX_TESTS_COMMAND
int Process_RunApp5(int argc
, wchar_t **argv
) {
180 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
182 if ((NULL
== argv
) || (NULL
== argv
[0])) {
183 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
185 base::string16 path
= MakeFullPathToSystem32(argv
[0]);
187 // TEST 5: Try with the path in the cmd_line and arguments.
188 base::string16 cmd_line
= L
"\"";
190 cmd_line
+= L
"\" /I";
191 return CreateProcessHelper(base::string16(), cmd_line
);
194 SBOX_TESTS_COMMAND
int Process_RunApp6(int argc
, wchar_t **argv
) {
196 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
198 if ((NULL
== argv
) || (NULL
== argv
[0])) {
199 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
202 // TEST 6: Try with the file_name in the cmd_line and arguments.
203 base::string16 cmd_line
= argv
[0];
205 return CreateProcessHelper(base::string16(), cmd_line
);
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
) {
211 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
213 if ((NULL
== argv
) || (NULL
== argv
[0]))
214 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
216 base::string16 path
= MakeFullPathToSystem32(argv
[0]);
218 STARTUPINFOW si
= {sizeof(si
)};
220 PROCESS_INFORMATION temp_process_info
= {};
221 if (!::CreateProcessW(path
.c_str(), NULL
, NULL
, NULL
, FALSE
, CREATE_SUSPENDED
,
222 NULL
, NULL
, &si
, &temp_process_info
)) {
223 return SBOX_TEST_FAILED
;
225 base::win::ScopedProcessInformation
pi(temp_process_info
);
229 ::OpenProcessToken(pi
.process_handle(), TOKEN_IMPERSONATE
, &token
);
230 DWORD error
= ::GetLastError();
232 base::win::ScopedHandle
token_handle(token
);
234 if (!::TerminateProcess(pi
.process_handle(), 0))
235 return SBOX_TEST_FAILED
;
238 return SBOX_TEST_SUCCEEDED
;
240 if (ERROR_ACCESS_DENIED
== error
)
241 return SBOX_TEST_DENIED
;
243 return SBOX_TEST_FAILED
;
247 SBOX_TESTS_COMMAND
int Process_OpenToken(int argc
, wchar_t **argv
) {
249 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS
, &token
)) {
250 if (ERROR_ACCESS_DENIED
== ::GetLastError()) {
251 return SBOX_TEST_DENIED
;
254 ::CloseHandle(token
);
255 return SBOX_TEST_SUCCEEDED
;
258 return SBOX_TEST_FAILED
;
261 TEST(ProcessPolicyTest
, TestAllAccess
) {
262 // Check if the "all access" rule fails to be added when the token is too
266 // Check the failing case.
267 runner
.GetPolicy()->SetTokenLevel(USER_INTERACTIVE
, USER_LOCKDOWN
);
268 EXPECT_EQ(SBOX_ERROR_UNSUPPORTED
,
269 runner
.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS
,
270 TargetPolicy::PROCESS_ALL_EXEC
,
271 L
"this is not important"));
273 // Check the working case.
274 runner
.GetPolicy()->SetTokenLevel(USER_INTERACTIVE
, USER_INTERACTIVE
);
276 EXPECT_EQ(SBOX_ALL_OK
,
277 runner
.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS
,
278 TargetPolicy::PROCESS_ALL_EXEC
,
279 L
"this is not important"));
282 TEST(ProcessPolicyTest
, CreateProcessAW
) {
284 base::string16 exe_path
= MakeFullPathToSystem32(L
"findstr.exe");
285 base::string16 system32
= MakeFullPathToSystem32(L
"");
286 ASSERT_TRUE(!exe_path
.empty());
287 EXPECT_TRUE(runner
.AddRule(TargetPolicy::SUBSYS_PROCESS
,
288 TargetPolicy::PROCESS_MIN_EXEC
,
291 // Need to add directory rules for the directories that we use in
292 // SetCurrentDirectory.
293 EXPECT_TRUE(runner
.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY
,
296 wchar_t current_directory
[MAX_PATH
];
297 DWORD ret
= ::GetCurrentDirectory(MAX_PATH
, current_directory
);
298 ASSERT_TRUE(0 != ret
&& ret
< MAX_PATH
);
300 wcscat_s(current_directory
, MAX_PATH
, L
"\\");
301 EXPECT_TRUE(runner
.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY
,
304 EXPECT_EQ(SBOX_TEST_DENIED
, runner
.RunTest(L
"Process_RunApp1 calc.exe"));
305 EXPECT_EQ(SBOX_TEST_DENIED
, runner
.RunTest(L
"Process_RunApp2 calc.exe"));
306 EXPECT_EQ(SBOX_TEST_DENIED
, runner
.RunTest(L
"Process_RunApp3 calc.exe"));
307 EXPECT_EQ(SBOX_TEST_DENIED
, runner
.RunTest(L
"Process_RunApp5 calc.exe"));
308 EXPECT_EQ(SBOX_TEST_DENIED
, runner
.RunTest(L
"Process_RunApp6 calc.exe"));
310 EXPECT_EQ(SBOX_TEST_SUCCEEDED
,
311 runner
.RunTest(L
"Process_RunApp1 findstr.exe"));
312 EXPECT_EQ(SBOX_TEST_SUCCEEDED
,
313 runner
.RunTest(L
"Process_RunApp2 findstr.exe"));
314 EXPECT_EQ(SBOX_TEST_SUCCEEDED
,
315 runner
.RunTest(L
"Process_RunApp3 findstr.exe"));
316 EXPECT_EQ(SBOX_TEST_SUCCEEDED
,
317 runner
.RunTest(L
"Process_RunApp5 findstr.exe"));
318 EXPECT_EQ(SBOX_TEST_SUCCEEDED
,
319 runner
.RunTest(L
"Process_RunApp6 findstr.exe"));
322 if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA
) {
323 // WinXP results are not reliable.
324 EXPECT_EQ(SBOX_TEST_SECOND_ERROR
,
325 runner
.RunTest(L
"Process_RunApp4 calc.exe"));
326 EXPECT_EQ(SBOX_TEST_SECOND_ERROR
,
327 runner
.RunTest(L
"Process_RunApp4 findstr.exe"));
332 TEST(ProcessPolicyTest
, OpenToken
) {
334 EXPECT_EQ(SBOX_TEST_SUCCEEDED
, runner
.RunTest(L
"Process_OpenToken"));
337 TEST(ProcessPolicyTest
, TestGetProcessTokenMinAccess
) {
339 base::string16 exe_path
= MakeFullPathToSystem32(L
"findstr.exe");
340 ASSERT_TRUE(!exe_path
.empty());
341 EXPECT_TRUE(runner
.AddRule(TargetPolicy::SUBSYS_PROCESS
,
342 TargetPolicy::PROCESS_MIN_EXEC
,
345 EXPECT_EQ(SBOX_TEST_DENIED
,
346 runner
.RunTest(L
"Process_GetChildProcessToken findstr.exe"));
349 TEST(ProcessPolicyTest
, TestGetProcessTokenMaxAccess
) {
350 TestRunner
runner(JOB_UNPROTECTED
, USER_INTERACTIVE
, USER_INTERACTIVE
);
351 base::string16 exe_path
= MakeFullPathToSystem32(L
"findstr.exe");
352 ASSERT_TRUE(!exe_path
.empty());
353 EXPECT_TRUE(runner
.AddRule(TargetPolicy::SUBSYS_PROCESS
,
354 TargetPolicy::PROCESS_ALL_EXEC
,
357 EXPECT_EQ(SBOX_TEST_SUCCEEDED
,
358 runner
.RunTest(L
"Process_GetChildProcessToken findstr.exe"));
361 TEST(ProcessPolicyTest
, TestGetProcessTokenMinAccessNoJob
) {
362 TestRunner
runner(JOB_NONE
, USER_RESTRICTED_SAME_ACCESS
, USER_LOCKDOWN
);
363 base::string16 exe_path
= MakeFullPathToSystem32(L
"findstr.exe");
364 ASSERT_TRUE(!exe_path
.empty());
365 EXPECT_TRUE(runner
.AddRule(TargetPolicy::SUBSYS_PROCESS
,
366 TargetPolicy::PROCESS_MIN_EXEC
,
369 EXPECT_EQ(SBOX_TEST_DENIED
,
370 runner
.RunTest(L
"Process_GetChildProcessToken findstr.exe"));
373 TEST(ProcessPolicyTest
, TestGetProcessTokenMaxAccessNoJob
) {
374 TestRunner
runner(JOB_NONE
, USER_INTERACTIVE
, USER_INTERACTIVE
);
375 base::string16 exe_path
= MakeFullPathToSystem32(L
"findstr.exe");
376 ASSERT_TRUE(!exe_path
.empty());
377 EXPECT_TRUE(runner
.AddRule(TargetPolicy::SUBSYS_PROCESS
,
378 TargetPolicy::PROCESS_ALL_EXEC
,
381 EXPECT_EQ(SBOX_TEST_SUCCEEDED
,
382 runner
.RunTest(L
"Process_GetChildProcessToken findstr.exe"));
385 } // namespace sandbox