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