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