1 // Copyright (c) 2011 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 <algorithm>
6 #include <cctype>
7 
8 #include <windows.h>
9 #include <winioctl.h>
10 
11 #include "base/win/scoped_handle.h"
12 #include "base/win/windows_version.h"
13 #include "sandbox/win/src/filesystem_policy.h"
14 #include "sandbox/win/src/nt_internals.h"
15 #include "sandbox/win/src/sandbox.h"
16 #include "sandbox/win/src/sandbox_factory.h"
17 #include "sandbox/win/src/sandbox_policy.h"
18 #include "sandbox/win/src/win_utils.h"
19 #include "sandbox/win/tests/common/controller.h"
20 #include "sandbox/win/tests/common/test_utils.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 #define BINDNTDLL(name)                                   \
24   name##Function name = reinterpret_cast<name##Function>( \
25       ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
26 
27 namespace sandbox {
28 
29 const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
30 
31 // Creates a file using different desired access. Returns if the call succeeded
32 // or not.  The first argument in argv is the filename. The second argument
33 // determines the type of access and the dispositino of the file.
File_Create(int argc,wchar_t ** argv)34 SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t** argv) {
35   if (argc != 2)
36     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
37 
38   std::wstring operation(argv[0]);
39 
40   if (operation == L"Read") {
41     base::win::ScopedHandle file1(CreateFile(
42         argv[1], GENERIC_READ, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
43     base::win::ScopedHandle file2(CreateFile(
44         argv[1], FILE_EXECUTE, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
45 
46     if (file1.IsValid() == file2.IsValid())
47       return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
48     return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
49 
50   } else if (operation == L"Write") {
51     base::win::ScopedHandle file1(CreateFile(
52         argv[1], GENERIC_ALL, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
53     base::win::ScopedHandle file2(
54         CreateFile(argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, nullptr,
55                    OPEN_EXISTING, 0, nullptr));
56 
57     if (file1.IsValid() == file2.IsValid())
58       return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
59     return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
60 
61   } else if (operation == L"ReadCreate") {
62     base::win::ScopedHandle file2(CreateFile(argv[1], GENERIC_READ, kSharing,
63                                              nullptr, CREATE_NEW, 0, nullptr));
64     base::win::ScopedHandle file1(CreateFile(
65         argv[1], GENERIC_READ, kSharing, nullptr, CREATE_ALWAYS, 0, nullptr));
66 
67     if (file1.IsValid() == file2.IsValid())
68       return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
69     return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
70   }
71 
72   return SBOX_TEST_INVALID_PARAMETER;
73 }
74 
File_Win32Create(int argc,wchar_t ** argv)75 SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t** argv) {
76   if (argc != 1) {
77     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
78   }
79 
80   std::wstring full_path = MakePathToSys(argv[0], false);
81   if (full_path.empty()) {
82     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
83   }
84 
85   HANDLE file =
86       ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, nullptr,
87                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
88 
89   if (INVALID_HANDLE_VALUE != file) {
90     ::CloseHandle(file);
91     return SBOX_TEST_SUCCEEDED;
92   } else {
93     if (ERROR_ACCESS_DENIED == ::GetLastError()) {
94       return SBOX_TEST_DENIED;
95     } else {
96       return SBOX_TEST_FAILED;
97     }
98   }
99   return SBOX_TEST_SUCCEEDED;
100 }
101 
102 // Creates the file in parameter using the NtCreateFile api and returns if the
103 // call succeeded or not.
File_CreateSys32(int argc,wchar_t ** argv)104 SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t** argv) {
105   BINDNTDLL(NtCreateFile);
106   BINDNTDLL(RtlInitUnicodeString);
107   if (!NtCreateFile || !RtlInitUnicodeString)
108     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
109 
110   if (argc != 1)
111     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
112 
113   std::wstring file(argv[0]);
114   if (0 != _wcsnicmp(file.c_str(), kNTDevicePrefix, kNTDevicePrefixLen))
115     file = MakePathToSys(argv[0], true);
116 
117   UNICODE_STRING object_name;
118   RtlInitUnicodeString(&object_name, file.c_str());
119 
120   OBJECT_ATTRIBUTES obj_attributes = {};
121   InitializeObjectAttributes(&obj_attributes, &object_name,
122                              OBJ_CASE_INSENSITIVE, nullptr, nullptr);
123 
124   HANDLE handle;
125   IO_STATUS_BLOCK io_block = {};
126   NTSTATUS status =
127       NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes, &io_block, nullptr,
128                    0, kSharing, FILE_OPEN, 0, nullptr, 0);
129   if (NT_SUCCESS(status)) {
130     ::CloseHandle(handle);
131     return SBOX_TEST_SUCCEEDED;
132   } else if (STATUS_ACCESS_DENIED == status) {
133     return SBOX_TEST_DENIED;
134   } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
135     return SBOX_TEST_NOT_FOUND;
136   }
137   return SBOX_TEST_FAILED;
138 }
139 
140 // Opens the file in parameter using the NtOpenFile api and returns if the
141 // call succeeded or not.
File_OpenSys32(int argc,wchar_t ** argv)142 SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t** argv) {
143   BINDNTDLL(NtOpenFile);
144   BINDNTDLL(RtlInitUnicodeString);
145   if (!NtOpenFile || !RtlInitUnicodeString)
146     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
147 
148   if (argc != 1)
149     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
150 
151   std::wstring file = MakePathToSys(argv[0], true);
152   UNICODE_STRING object_name;
153   RtlInitUnicodeString(&object_name, file.c_str());
154 
155   OBJECT_ATTRIBUTES obj_attributes = {};
156   InitializeObjectAttributes(&obj_attributes, &object_name,
157                              OBJ_CASE_INSENSITIVE, nullptr, nullptr);
158 
159   HANDLE handle;
160   IO_STATUS_BLOCK io_block = {};
161   NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes,
162                                &io_block, kSharing, 0);
163   if (NT_SUCCESS(status)) {
164     ::CloseHandle(handle);
165     return SBOX_TEST_SUCCEEDED;
166   } else if (STATUS_ACCESS_DENIED == status) {
167     return SBOX_TEST_DENIED;
168   } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
169     return SBOX_TEST_NOT_FOUND;
170   }
171   return SBOX_TEST_FAILED;
172 }
173 
File_GetDiskSpace(int argc,wchar_t ** argv)174 SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t** argv) {
175   std::wstring sys_path = MakePathToSys(L"", false);
176   if (sys_path.empty()) {
177     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
178   }
179   ULARGE_INTEGER free_user = {};
180   ULARGE_INTEGER total = {};
181   ULARGE_INTEGER free_total = {};
182   if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total,
183                             &free_total)) {
184     if ((total.QuadPart != 0) && (free_total.QuadPart != 0)) {
185       return SBOX_TEST_SUCCEEDED;
186     }
187   } else {
188     if (ERROR_ACCESS_DENIED == ::GetLastError()) {
189       return SBOX_TEST_DENIED;
190     } else {
191       return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
192     }
193   }
194   return SBOX_TEST_SUCCEEDED;
195 }
196 
197 // Move a file using the MoveFileEx api and returns if the call succeeded or
198 // not.
File_Rename(int argc,wchar_t ** argv)199 SBOX_TESTS_COMMAND int File_Rename(int argc, wchar_t** argv) {
200   if (argc != 2)
201     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
202 
203   if (::MoveFileEx(argv[0], argv[1], 0))
204     return SBOX_TEST_SUCCEEDED;
205 
206   if (::GetLastError() != ERROR_ACCESS_DENIED)
207     return SBOX_TEST_FAILED;
208 
209   return SBOX_TEST_DENIED;
210 }
211 
212 // Query the attributes of file in parameter using the NtQueryAttributesFile api
213 // and NtQueryFullAttributesFile and returns if the call succeeded or not. The
214 // second argument in argv is "d" or "f" telling if we expect the attributes to
215 // specify a file or a directory. The expected attribute has to match the real
216 // attributes for the call to be successful.
File_QueryAttributes(int argc,wchar_t ** argv)217 SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t** argv) {
218   BINDNTDLL(NtQueryAttributesFile);
219   BINDNTDLL(NtQueryFullAttributesFile);
220   BINDNTDLL(RtlInitUnicodeString);
221   if (!NtQueryAttributesFile || !NtQueryFullAttributesFile ||
222       !RtlInitUnicodeString)
223     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
224 
225   if (argc != 2)
226     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
227 
228   bool expect_directory = (L'd' == argv[1][0]);
229 
230   UNICODE_STRING object_name;
231   std::wstring file = MakePathToSys(argv[0], true);
232   RtlInitUnicodeString(&object_name, file.c_str());
233 
234   OBJECT_ATTRIBUTES obj_attributes = {};
235   InitializeObjectAttributes(&obj_attributes, &object_name,
236                              OBJ_CASE_INSENSITIVE, nullptr, nullptr);
237 
238   FILE_BASIC_INFORMATION info = {};
239   FILE_NETWORK_OPEN_INFORMATION full_info = {};
240   NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info);
241   NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info);
242 
243   if (status1 != status2)
244     return SBOX_TEST_FAILED;
245 
246   if (NT_SUCCESS(status1)) {
247     if (info.FileAttributes != full_info.FileAttributes)
248       return SBOX_TEST_FAILED;
249 
250     bool is_directory1 = (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
251     if (expect_directory == is_directory1)
252       return SBOX_TEST_SUCCEEDED;
253   } else if (STATUS_ACCESS_DENIED == status1) {
254     return SBOX_TEST_DENIED;
255   } else if (STATUS_OBJECT_NAME_NOT_FOUND == status1) {
256     return SBOX_TEST_NOT_FOUND;
257   }
258 
259   return SBOX_TEST_FAILED;
260 }
261 
262 // Tries to create a backup of calc.exe in system32 folder. This should fail
263 // with ERROR_ACCESS_DENIED if everything is working as expected.
File_CopyFile(int argc,wchar_t ** argv)264 SBOX_TESTS_COMMAND int File_CopyFile(int argc, wchar_t** argv) {
265   std::wstring calc_path = MakePathToSys(L"calc.exe", false);
266   std::wstring calc_backup_path = MakePathToSys(L"calc.exe.bak", false);
267 
268   if (::CopyFile(calc_path.c_str(), calc_backup_path.c_str(), FALSE))
269     return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
270 
271   if (::GetLastError() != ERROR_ACCESS_DENIED)
272     return SBOX_TEST_FAILED;
273 
274   return SBOX_TEST_SUCCEEDED;
275 }
276 
TEST(FilePolicyTest,DenyNtCreateCalc)277 TEST(FilePolicyTest, DenyNtCreateCalc) {
278   TestRunner runner;
279   EXPECT_TRUE(
280       runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"calc.exe"));
281 
282   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 calc.exe"));
283 
284   runner.SetTestState(BEFORE_REVERT);
285   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
286 }
287 
TEST(FilePolicyTest,AllowNtCreateCalc)288 TEST(FilePolicyTest, AllowNtCreateCalc) {
289   TestRunner runner;
290   EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe"));
291 
292   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
293 
294   runner.SetTestState(BEFORE_REVERT);
295   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
296 }
297 
TEST(FilePolicyTest,AllowNtCreateWithNativePath)298 TEST(FilePolicyTest, AllowNtCreateWithNativePath) {
299   std::wstring calc = MakePathToSys(L"calc.exe", false);
300   std::wstring nt_path;
301   ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path));
302   TestRunner runner;
303   runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str());
304 
305   wchar_t buff[MAX_PATH];
306   ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
307   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
308 
309   for (wchar_t& c : nt_path)
310     c = std::tolower(c);
311   ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
312   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
313 }
314 
TEST(FilePolicyTest,AllowReadOnly)315 TEST(FilePolicyTest, AllowReadOnly) {
316   TestRunner runner;
317 
318   // Create a temp file because we need write access to it.
319   wchar_t temp_directory[MAX_PATH];
320   wchar_t temp_file_name[MAX_PATH];
321   ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
322   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
323 
324   EXPECT_TRUE(
325       runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name));
326 
327   wchar_t command_read[MAX_PATH + 20] = {};
328   wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name);
329   wchar_t command_read_create[MAX_PATH + 20] = {};
330   wsprintf(command_read_create, L"File_Create ReadCreate \"%ls\"",
331            temp_file_name);
332   wchar_t command_write[MAX_PATH + 20] = {};
333   wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
334 
335   // Verify that we cannot create the file after revert.
336   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_read_create));
337 
338   // Verify that we don't have write access after revert.
339   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write));
340 
341   // Verify that we have read access after revert.
342   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read));
343 
344   // Verify that we really have write access to the file.
345   runner.SetTestState(BEFORE_REVERT);
346   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
347 
348   DeleteFile(temp_file_name);
349 }
350 
351 // Tests support of "\\\\.\\DeviceName" kind of paths.
TEST(FilePolicyTest,AllowImplicitDeviceName)352 TEST(FilePolicyTest, AllowImplicitDeviceName) {
353   TestRunner runner;
354 
355   wchar_t temp_directory[MAX_PATH];
356   wchar_t temp_file_name[MAX_PATH];
357   ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
358   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
359 
360   std::wstring path(temp_file_name);
361   EXPECT_TRUE(ConvertToLongPath(&path));
362   EXPECT_TRUE(GetNtPathFromWin32Path(path, &path));
363   path = path.substr(sandbox::kNTDevicePrefixLen);
364 
365   wchar_t command[MAX_PATH + 20] = {};
366   wsprintf(command, L"File_Create Read \"\\\\.\\%ls\"", path.c_str());
367   path = std::wstring(kNTPrefix) + path;
368 
369   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
370   EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, path.c_str()));
371   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
372 
373   DeleteFile(temp_file_name);
374 }
375 
TEST(FilePolicyTest,AllowWildcard)376 TEST(FilePolicyTest, AllowWildcard) {
377   TestRunner runner;
378 
379   // Create a temp file because we need write access to it.
380   wchar_t temp_directory[MAX_PATH];
381   wchar_t temp_file_name[MAX_PATH];
382   ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
383   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
384 
385   wcscat_s(temp_directory, MAX_PATH, L"*");
386   EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_directory));
387 
388   wchar_t command_write[MAX_PATH + 20] = {};
389   wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
390 
391   // Verify that we have write access after revert.
392   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
393 
394   DeleteFile(temp_file_name);
395 }
396 
TEST(FilePolicyTest,AllowNtCreatePatternRule)397 TEST(FilePolicyTest, AllowNtCreatePatternRule) {
398   TestRunner runner;
399   EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll"));
400 
401   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
402             runner.RunTest(L"File_OpenSys32 apphelp.dll"));
403   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
404 
405   runner.SetTestState(BEFORE_REVERT);
406   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
407             runner.RunTest(L"File_OpenSys32 apphelp.dll"));
408   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
409 }
410 
TEST(FilePolicyTest,CheckNotFound)411 TEST(FilePolicyTest, CheckNotFound) {
412   TestRunner runner;
413   EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"n*.dll"));
414 
415   EXPECT_EQ(SBOX_TEST_NOT_FOUND,
416             runner.RunTest(L"File_OpenSys32 notfound.dll"));
417 }
418 
TEST(FilePolicyTest,CheckNoLeak)419 TEST(FilePolicyTest, CheckNoLeak) {
420   TestRunner runner;
421   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe"));
422 }
423 
TEST(FilePolicyTest,TestQueryAttributesFile)424 TEST(FilePolicyTest, TestQueryAttributesFile) {
425   TestRunner runner;
426   EXPECT_TRUE(
427       runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"apphelp.dll"));
428   EXPECT_TRUE(
429       runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notfound.exe"));
430   EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"drivers"));
431   EXPECT_TRUE(
432       runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY, L"ipconfig.exe"));
433 
434   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
435             runner.RunTest(L"File_QueryAttributes drivers d"));
436 
437   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
438             runner.RunTest(L"File_QueryAttributes apphelp.dll f"));
439 
440   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
441             runner.RunTest(L"File_QueryAttributes ipconfig.exe f"));
442 
443   EXPECT_EQ(SBOX_TEST_DENIED,
444             runner.RunTest(L"File_QueryAttributes ftp.exe f"));
445 
446   EXPECT_EQ(SBOX_TEST_NOT_FOUND,
447             runner.RunTest(L"File_QueryAttributes notfound.exe f"));
448 }
449 
450 // Makes sure that we don't leak information when there is not policy to allow
451 // a path.
TEST(FilePolicyTest,TestQueryAttributesFileNoPolicy)452 TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) {
453   TestRunner runner;
454   EXPECT_EQ(SBOX_TEST_DENIED,
455             runner.RunTest(L"File_QueryAttributes ftp.exe f"));
456 
457   EXPECT_EQ(SBOX_TEST_DENIED,
458             runner.RunTest(L"File_QueryAttributes notfound.exe f"));
459 }
460 
TEST(FilePolicyTest,TestRename)461 TEST(FilePolicyTest, TestRename) {
462   TestRunner runner;
463 
464   // Give access to the temp directory.
465   wchar_t temp_directory[MAX_PATH];
466   wchar_t temp_file_name1[MAX_PATH];
467   wchar_t temp_file_name2[MAX_PATH];
468   wchar_t temp_file_name3[MAX_PATH];
469   wchar_t temp_file_name4[MAX_PATH];
470   wchar_t temp_file_name5[MAX_PATH];
471   wchar_t temp_file_name6[MAX_PATH];
472   wchar_t temp_file_name7[MAX_PATH];
473   wchar_t temp_file_name8[MAX_PATH];
474   ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
475   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name1), 0u);
476   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name2), 0u);
477   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name3), 0u);
478   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name4), 0u);
479   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name5), 0u);
480   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name6), 0u);
481   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name7), 0u);
482   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name8), 0u);
483 
484   // Add rules to make file1->file2 succeed.
485   ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name1));
486   ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name2));
487 
488   // Add rules to make file3->file4 fail.
489   ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name3));
490   ASSERT_TRUE(
491       runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name4));
492 
493   // Add rules to make file5->file6 fail.
494   ASSERT_TRUE(
495       runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name5));
496   ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name6));
497 
498   // Add rules to make file7->no_pol_file fail.
499   ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name7));
500 
501   // Delete the files where the files are going to be renamed to.
502   ::DeleteFile(temp_file_name2);
503   ::DeleteFile(temp_file_name4);
504   ::DeleteFile(temp_file_name6);
505   ::DeleteFile(temp_file_name8);
506 
507   wchar_t command[MAX_PATH * 2 + 20] = {};
508   wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1,
509            temp_file_name2);
510   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
511 
512   wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name3,
513            temp_file_name4);
514   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
515 
516   wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name5,
517            temp_file_name6);
518   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
519 
520   wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name7,
521            temp_file_name8);
522   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
523 
524   // Delete all the files in case they are still there.
525   ::DeleteFile(temp_file_name1);
526   ::DeleteFile(temp_file_name2);
527   ::DeleteFile(temp_file_name3);
528   ::DeleteFile(temp_file_name4);
529   ::DeleteFile(temp_file_name5);
530   ::DeleteFile(temp_file_name6);
531   ::DeleteFile(temp_file_name7);
532   ::DeleteFile(temp_file_name8);
533 }
534 
TEST(FilePolicyTest,OpenSys32FilesDenyBecauseOfDir)535 TEST(FilePolicyTest, OpenSys32FilesDenyBecauseOfDir) {
536   TestRunner runner;
537   EXPECT_TRUE(
538       runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"notepad.exe"));
539 
540   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
541 
542   runner.SetTestState(BEFORE_REVERT);
543   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
544             runner.RunTest(L"File_Win32Create notepad.exe"));
545 }
546 
TEST(FilePolicyTest,OpenSys32FilesAllowNotepad)547 TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) {
548   TestRunner runner;
549   EXPECT_TRUE(
550       runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notepad.exe"));
551 
552   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
553             runner.RunTest(L"File_Win32Create notepad.exe"));
554 
555   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe"));
556 
557   runner.SetTestState(BEFORE_REVERT);
558   EXPECT_EQ(SBOX_TEST_SUCCEEDED,
559             runner.RunTest(L"File_Win32Create notepad.exe"));
560   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_Win32Create calc.exe"));
561 }
562 
TEST(FilePolicyTest,FileGetDiskSpace)563 TEST(FilePolicyTest, FileGetDiskSpace) {
564   TestRunner runner;
565   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_GetDiskSpace"));
566   runner.SetTestState(BEFORE_REVERT);
567   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
568 
569   // Add an 'allow' rule in the windows\system32 such that GetDiskFreeSpaceEx
570   // succeeds (it does an NtOpenFile) but windows\system32\notepad.exe is
571   // denied since there is no wild card in the rule.
572   EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L""));
573   runner.SetTestState(BEFORE_REVERT);
574   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
575 
576   runner.SetTestState(AFTER_REVERT);
577   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
578   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
579 }
580 
TEST(FilePolicyTest,TestReparsePoint)581 TEST(FilePolicyTest, TestReparsePoint) {
582   TestRunner runner;
583 
584   // Create a temp file because we need write access to it.
585   wchar_t temp_directory[MAX_PATH];
586   wchar_t temp_file_name[MAX_PATH];
587   ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
588   ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
589 
590   // Delete the file and create a directory instead.
591   ASSERT_TRUE(::DeleteFile(temp_file_name));
592   ASSERT_TRUE(::CreateDirectory(temp_file_name, nullptr));
593 
594   // Create a temporary file in the subfolder.
595   std::wstring subfolder = temp_file_name;
596   std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1);
597   std::wstring temp_file = subfolder + L"\\file_" + temp_file_title;
598 
599   HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS,
600                              FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
601                              CREATE_ALWAYS, 0, nullptr);
602   ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
603   ASSERT_TRUE(::CloseHandle(file));
604 
605   // Create a temporary file in the temp directory.
606   std::wstring temp_dir = temp_directory;
607   std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title;
608   file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS,
609                       FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
610                       CREATE_ALWAYS, 0, nullptr);
611   ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
612   ASSERT_TRUE(::CloseHandle(file));
613 
614   // Give write access to the temp directory.
615   std::wstring temp_dir_wildcard = temp_dir + L"*";
616   EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY,
617                                temp_dir_wildcard.c_str()));
618 
619   // Prepare the command to execute.
620   std::wstring command_write;
621   command_write += L"File_Create Write \"";
622   command_write += temp_file;
623   command_write += L"\"";
624 
625   // Verify that we have write access to the original file
626   EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str()));
627 
628   // Replace the subfolder by a reparse point to %temp%.
629   ::DeleteFile(temp_file.c_str());
630   HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
631                             FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
632                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
633   EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
634 
635   std::wstring temp_dir_nt;
636   temp_dir_nt += L"\\??\\";
637   temp_dir_nt += temp_dir;
638   EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
639   EXPECT_TRUE(::CloseHandle(dir));
640 
641   // Try to open the file again.
642   EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str()));
643 
644   // Remove the reparse point.
645   dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
646                      FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
647                      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
648                      nullptr);
649   EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
650   EXPECT_TRUE(DeleteReparsePoint(dir));
651   EXPECT_TRUE(::CloseHandle(dir));
652 
653   // Cleanup.
654   EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str()));
655   EXPECT_TRUE(::RemoveDirectory(subfolder.c_str()));
656 }
657 
TEST(FilePolicyTest,CheckExistingNTPrefixEscape)658 TEST(FilePolicyTest, CheckExistingNTPrefixEscape) {
659   std::wstring name = L"\\??\\NAME";
660 
661   std::wstring result = FixNTPrefixForMatch(name);
662 
663   EXPECT_STREQ(result.c_str(), L"\\/?/?\\NAME");
664 }
665 
TEST(FilePolicyTest,CheckEscapedNTPrefixNoEscape)666 TEST(FilePolicyTest, CheckEscapedNTPrefixNoEscape) {
667   std::wstring name = L"\\/?/?\\NAME";
668 
669   std::wstring result = FixNTPrefixForMatch(name);
670 
671   EXPECT_STREQ(result.c_str(), name.c_str());
672 }
673 
TEST(FilePolicyTest,CheckMissingNTPrefixEscape)674 TEST(FilePolicyTest, CheckMissingNTPrefixEscape) {
675   std::wstring name = L"C:\\NAME";
676 
677   std::wstring result = FixNTPrefixForMatch(name);
678 
679   EXPECT_STREQ(result.c_str(), L"\\/?/?\\C:\\NAME");
680 }
681 
TEST(FilePolicyTest,TestCopyFile)682 TEST(FilePolicyTest, TestCopyFile) {
683   // Check if the test is running Win8 or newer since
684   // MITIGATION_STRICT_HANDLE_CHECKS is not supported on older systems.
685   if (base::win::GetVersion() < base::win::Version::WIN8)
686     return;
687 
688   TestRunner runner;
689   runner.SetTimeout(2000);
690 
691   // Allow read access to calc.exe, this should be on all Windows versions.
692   ASSERT_TRUE(
693       runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_READONLY, L"calc.exe"));
694 
695   sandbox::TargetPolicy* policy = runner.GetPolicy();
696 
697   // Set proper mitigation.
698   EXPECT_EQ(
699       policy->SetDelayedProcessMitigations(MITIGATION_STRICT_HANDLE_CHECKS),
700       SBOX_ALL_OK);
701 
702   ASSERT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CopyFile"));
703 }
704 
705 }  // namespace sandbox
706