michael@0: // Copyright (c) 2011 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "base/win/scoped_handle.h" michael@0: #include "sandbox/win/src/nt_internals.h" michael@0: #include "sandbox/win/src/sandbox.h" michael@0: #include "sandbox/win/src/sandbox_factory.h" michael@0: #include "sandbox/win/src/sandbox_policy.h" michael@0: #include "sandbox/win/src/win_utils.h" michael@0: #include "sandbox/win/tests/common/controller.h" michael@0: #include "sandbox/win/tests/common/test_utils.h" michael@0: #include "testing/gtest/include/gtest/gtest.h" michael@0: michael@0: #define BINDNTDLL(name) \ michael@0: name ## Function name = reinterpret_cast( \ michael@0: ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) michael@0: michael@0: namespace sandbox { michael@0: michael@0: const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE; michael@0: michael@0: // Creates a file using different desired access. Returns if the call succeeded michael@0: // or not. The first argument in argv is the filename. If the second argument michael@0: // is "read", we try read only access. Otherwise we try read-write access. michael@0: SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t **argv) { michael@0: if (argc != 2) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: bool read = (_wcsicmp(argv[0], L"Read") == 0); michael@0: michael@0: if (read) { michael@0: base::win::ScopedHandle file1(CreateFile( michael@0: argv[1], GENERIC_READ, kSharing, NULL, OPEN_EXISTING, 0, NULL)); michael@0: base::win::ScopedHandle file2(CreateFile( michael@0: argv[1], FILE_EXECUTE, kSharing, NULL, OPEN_EXISTING, 0, NULL)); michael@0: michael@0: if (file1.Get() && file2.Get()) michael@0: return SBOX_TEST_SUCCEEDED; michael@0: return SBOX_TEST_DENIED; michael@0: } else { michael@0: base::win::ScopedHandle file1(CreateFile( michael@0: argv[1], GENERIC_ALL, kSharing, NULL, OPEN_EXISTING, 0, NULL)); michael@0: base::win::ScopedHandle file2(CreateFile( michael@0: argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, NULL, OPEN_EXISTING, michael@0: 0, NULL)); michael@0: michael@0: if (file1.Get() && file2.Get()) michael@0: return SBOX_TEST_SUCCEEDED; michael@0: return SBOX_TEST_DENIED; michael@0: } michael@0: } michael@0: michael@0: SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { michael@0: if (argc != 1) { michael@0: SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: } michael@0: michael@0: std::wstring full_path = MakePathToSys(argv[0], false); michael@0: if (full_path.empty()) { michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: } michael@0: michael@0: HANDLE file = ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, michael@0: NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); michael@0: michael@0: if (INVALID_HANDLE_VALUE != file) { michael@0: ::CloseHandle(file); michael@0: return SBOX_TEST_SUCCEEDED; michael@0: } else { michael@0: if (ERROR_ACCESS_DENIED == ::GetLastError()) { michael@0: return SBOX_TEST_DENIED; michael@0: } else { michael@0: return SBOX_TEST_FAILED; michael@0: } michael@0: } michael@0: return SBOX_TEST_SUCCEEDED; michael@0: } michael@0: michael@0: // Creates the file in parameter using the NtCreateFile api and returns if the michael@0: // call succeeded or not. michael@0: SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { michael@0: BINDNTDLL(NtCreateFile); michael@0: BINDNTDLL(RtlInitUnicodeString); michael@0: if (!NtCreateFile || !RtlInitUnicodeString) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: if (argc != 1) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: std::wstring file(argv[0]); michael@0: if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) michael@0: file = MakePathToSys(argv[0], true); michael@0: michael@0: UNICODE_STRING object_name; michael@0: RtlInitUnicodeString(&object_name, file.c_str()); michael@0: michael@0: OBJECT_ATTRIBUTES obj_attributes = {0}; michael@0: InitializeObjectAttributes(&obj_attributes, &object_name, michael@0: OBJ_CASE_INSENSITIVE, NULL, NULL); michael@0: michael@0: HANDLE handle; michael@0: IO_STATUS_BLOCK io_block = {0}; michael@0: NTSTATUS status = NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes, michael@0: &io_block, NULL, 0, kSharing, FILE_OPEN, michael@0: 0, NULL, 0); michael@0: if (NT_SUCCESS(status)) { michael@0: ::CloseHandle(handle); michael@0: return SBOX_TEST_SUCCEEDED; michael@0: } else if (STATUS_ACCESS_DENIED == status) { michael@0: return SBOX_TEST_DENIED; michael@0: } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { michael@0: return SBOX_TEST_NOT_FOUND; michael@0: } michael@0: return SBOX_TEST_FAILED; michael@0: } michael@0: michael@0: // Opens the file in parameter using the NtOpenFile api and returns if the michael@0: // call succeeded or not. michael@0: SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { michael@0: BINDNTDLL(NtOpenFile); michael@0: BINDNTDLL(RtlInitUnicodeString); michael@0: if (!NtOpenFile || !RtlInitUnicodeString) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: if (argc != 1) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: std::wstring file = MakePathToSys(argv[0], true); michael@0: UNICODE_STRING object_name; michael@0: RtlInitUnicodeString(&object_name, file.c_str()); michael@0: michael@0: OBJECT_ATTRIBUTES obj_attributes = {0}; michael@0: InitializeObjectAttributes(&obj_attributes, &object_name, michael@0: OBJ_CASE_INSENSITIVE, NULL, NULL); michael@0: michael@0: HANDLE handle; michael@0: IO_STATUS_BLOCK io_block = {0}; michael@0: NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes, michael@0: &io_block, kSharing, 0); michael@0: if (NT_SUCCESS(status)) { michael@0: ::CloseHandle(handle); michael@0: return SBOX_TEST_SUCCEEDED; michael@0: } else if (STATUS_ACCESS_DENIED == status) { michael@0: return SBOX_TEST_DENIED; michael@0: } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { michael@0: return SBOX_TEST_NOT_FOUND; michael@0: } michael@0: return SBOX_TEST_FAILED; michael@0: } michael@0: michael@0: SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { michael@0: std::wstring sys_path = MakePathToSys(L"", false); michael@0: if (sys_path.empty()) { michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: } michael@0: ULARGE_INTEGER free_user = {0}; michael@0: ULARGE_INTEGER total = {0}; michael@0: ULARGE_INTEGER free_total = {0}; michael@0: if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total, michael@0: &free_total)) { michael@0: if ((total.QuadPart != 0) && (free_total.QuadPart !=0)) { michael@0: return SBOX_TEST_SUCCEEDED; michael@0: } michael@0: } else { michael@0: if (ERROR_ACCESS_DENIED == ::GetLastError()) { michael@0: return SBOX_TEST_DENIED; michael@0: } else { michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: } michael@0: } michael@0: return SBOX_TEST_SUCCEEDED; michael@0: } michael@0: michael@0: // Move a file using the MoveFileEx api and returns if the call succeeded or michael@0: // not. michael@0: SBOX_TESTS_COMMAND int File_Rename(int argc, wchar_t **argv) { michael@0: if (argc != 2) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: if (::MoveFileEx(argv[0], argv[1], 0)) michael@0: return SBOX_TEST_SUCCEEDED; michael@0: michael@0: if (::GetLastError() != ERROR_ACCESS_DENIED) michael@0: return SBOX_TEST_FAILED; michael@0: michael@0: return SBOX_TEST_DENIED; michael@0: } michael@0: michael@0: // Query the attributes of file in parameter using the NtQueryAttributesFile api michael@0: // and NtQueryFullAttributesFile and returns if the call succeeded or not. The michael@0: // second argument in argv is "d" or "f" telling if we expect the attributes to michael@0: // specify a file or a directory. The expected attribute has to match the real michael@0: // attributes for the call to be successful. michael@0: SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t **argv) { michael@0: BINDNTDLL(NtQueryAttributesFile); michael@0: BINDNTDLL(NtQueryFullAttributesFile); michael@0: BINDNTDLL(RtlInitUnicodeString); michael@0: if (!NtQueryAttributesFile || !NtQueryFullAttributesFile || michael@0: !RtlInitUnicodeString) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: if (argc != 2) michael@0: return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; michael@0: michael@0: bool expect_directory = (L'd' == argv[1][0]); michael@0: michael@0: UNICODE_STRING object_name; michael@0: std::wstring file = MakePathToSys(argv[0], true); michael@0: RtlInitUnicodeString(&object_name, file.c_str()); michael@0: michael@0: OBJECT_ATTRIBUTES obj_attributes = {0}; michael@0: InitializeObjectAttributes(&obj_attributes, &object_name, michael@0: OBJ_CASE_INSENSITIVE, NULL, NULL); michael@0: michael@0: FILE_BASIC_INFORMATION info = {0}; michael@0: FILE_NETWORK_OPEN_INFORMATION full_info = {0}; michael@0: NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info); michael@0: NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info); michael@0: michael@0: if (status1 != status2) michael@0: return SBOX_TEST_FAILED; michael@0: michael@0: if (NT_SUCCESS(status1)) { michael@0: if (info.FileAttributes != full_info.FileAttributes) michael@0: return SBOX_TEST_FAILED; michael@0: michael@0: bool is_directory1 = (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; michael@0: if (expect_directory == is_directory1) michael@0: return SBOX_TEST_SUCCEEDED; michael@0: } else if (STATUS_ACCESS_DENIED == status1) { michael@0: return SBOX_TEST_DENIED; michael@0: } else if (STATUS_OBJECT_NAME_NOT_FOUND == status1) { michael@0: return SBOX_TEST_NOT_FOUND; michael@0: } michael@0: michael@0: return SBOX_TEST_FAILED; michael@0: } michael@0: michael@0: TEST(FilePolicyTest, DenyNtCreateCalc) { michael@0: TestRunner runner; michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, michael@0: L"calc.exe")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 calc.exe")); michael@0: michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, AllowNtCreateCalc) { michael@0: TestRunner runner; michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); michael@0: michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, AllowNtCreateWithNativePath) { michael@0: std::wstring calc = MakePathToSys(L"calc.exe", false); michael@0: std::wstring nt_path; michael@0: ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path)); michael@0: TestRunner runner; michael@0: runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str()); michael@0: michael@0: wchar_t buff[MAX_PATH]; michael@0: ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); michael@0: michael@0: std::transform(nt_path.begin(), nt_path.end(), nt_path.begin(), std::tolower); michael@0: ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, AllowReadOnly) { michael@0: TestRunner runner; michael@0: michael@0: // Create a temp file because we need write access to it. michael@0: wchar_t temp_directory[MAX_PATH]; michael@0: wchar_t temp_file_name[MAX_PATH]; michael@0: ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); michael@0: michael@0: EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, michael@0: temp_file_name)); michael@0: michael@0: wchar_t command_read[MAX_PATH + 20] = {0}; michael@0: wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name); michael@0: wchar_t command_write[MAX_PATH + 20] = {0}; michael@0: wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); michael@0: michael@0: // Verify that we have read access after revert. michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read)); michael@0: michael@0: // Verify that we don't have write access after revert. michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write)); michael@0: michael@0: // Verify that we really have write access to the file. michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write)); michael@0: michael@0: DeleteFile(temp_file_name); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, AllowWildcard) { michael@0: TestRunner runner; michael@0: michael@0: // Create a temp file because we need write access to it. michael@0: wchar_t temp_directory[MAX_PATH]; michael@0: wchar_t temp_file_name[MAX_PATH]; michael@0: ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); michael@0: michael@0: wcscat_s(temp_directory, MAX_PATH, L"*"); michael@0: EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_directory)); michael@0: michael@0: wchar_t command_write[MAX_PATH + 20] = {0}; michael@0: wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); michael@0: michael@0: // Verify that we have write access after revert. michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write)); michael@0: michael@0: DeleteFile(temp_file_name); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, AllowNtCreatePatternRule) { michael@0: TestRunner runner; michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_OpenSys32 appmgmts.dll")); michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_OpenSys32 appwiz.cpl")); michael@0: michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_OpenSys32 appmgmts.dll")); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_OpenSys32 appwiz.cpl")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, CheckNotFound) { michael@0: TestRunner runner; michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"n*.dll")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_NOT_FOUND, michael@0: runner.RunTest(L"File_OpenSys32 notfound.dll")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, CheckNoLeak) { michael@0: TestRunner runner; michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, TestQueryAttributesFile) { michael@0: TestRunner runner; michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, michael@0: L"appmgmts.dll")); michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, michael@0: L"notfound.exe")); michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"drivers")); michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY, michael@0: L"ipconfig.exe")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_QueryAttributes drivers d")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_QueryAttributes appmgmts.dll f")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_QueryAttributes ipconfig.exe f")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_DENIED, michael@0: runner.RunTest(L"File_QueryAttributes ftp.exe f")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_NOT_FOUND, michael@0: runner.RunTest(L"File_QueryAttributes notfound.exe f")); michael@0: } michael@0: michael@0: // Makes sure that we don't leak information when there is not policy to allow michael@0: // a path. michael@0: TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) { michael@0: TestRunner runner; michael@0: EXPECT_EQ(SBOX_TEST_DENIED, michael@0: runner.RunTest(L"File_QueryAttributes ftp.exe f")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_DENIED, michael@0: runner.RunTest(L"File_QueryAttributes notfound.exe f")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, TestRename) { michael@0: TestRunner runner; michael@0: michael@0: // Give access to the temp directory. michael@0: wchar_t temp_directory[MAX_PATH]; michael@0: wchar_t temp_file_name1[MAX_PATH]; michael@0: wchar_t temp_file_name2[MAX_PATH]; michael@0: wchar_t temp_file_name3[MAX_PATH]; michael@0: wchar_t temp_file_name4[MAX_PATH]; michael@0: wchar_t temp_file_name5[MAX_PATH]; michael@0: wchar_t temp_file_name6[MAX_PATH]; michael@0: wchar_t temp_file_name7[MAX_PATH]; michael@0: wchar_t temp_file_name8[MAX_PATH]; michael@0: ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name1), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name2), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name3), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name4), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name5), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name6), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name7), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name8), 0u); michael@0: michael@0: michael@0: // Add rules to make file1->file2 succeed. michael@0: ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name1)); michael@0: ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name2)); michael@0: michael@0: // Add rules to make file3->file4 fail. michael@0: ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name3)); michael@0: ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, michael@0: temp_file_name4)); michael@0: michael@0: // Add rules to make file5->file6 fail. michael@0: ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, michael@0: temp_file_name5)); michael@0: ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name6)); michael@0: michael@0: // Add rules to make file7->no_pol_file fail. michael@0: ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name7)); michael@0: michael@0: // Delete the files where the files are going to be renamed to. michael@0: ::DeleteFile(temp_file_name2); michael@0: ::DeleteFile(temp_file_name4); michael@0: ::DeleteFile(temp_file_name6); michael@0: ::DeleteFile(temp_file_name8); michael@0: michael@0: michael@0: wchar_t command[MAX_PATH*2 + 20] = {0}; michael@0: wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1, michael@0: temp_file_name2); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command)); michael@0: michael@0: wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name3, michael@0: temp_file_name4); michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); michael@0: michael@0: wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name5, michael@0: temp_file_name6); michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); michael@0: michael@0: wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name7, michael@0: temp_file_name8); michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); michael@0: michael@0: michael@0: // Delete all the files in case they are still there. michael@0: ::DeleteFile(temp_file_name1); michael@0: ::DeleteFile(temp_file_name2); michael@0: ::DeleteFile(temp_file_name3); michael@0: ::DeleteFile(temp_file_name4); michael@0: ::DeleteFile(temp_file_name5); michael@0: ::DeleteFile(temp_file_name6); michael@0: ::DeleteFile(temp_file_name7); michael@0: ::DeleteFile(temp_file_name8); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, OpenSys32FilesDenyBecauseOfDir) { michael@0: TestRunner runner; michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, michael@0: L"notepad.exe")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe")); michael@0: michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_Win32Create notepad.exe")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) { michael@0: TestRunner runner; michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, michael@0: L"notepad.exe")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_Win32Create notepad.exe")); michael@0: michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe")); michael@0: michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, michael@0: runner.RunTest(L"File_Win32Create notepad.exe")); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_Win32Create calc.exe")); michael@0: } michael@0: michael@0: TEST(FilePolicyTest, FileGetDiskSpace) { michael@0: TestRunner runner; michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_GetDiskSpace")); michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); michael@0: michael@0: // Add an 'allow' rule in the windows\system32 such that GetDiskFreeSpaceEx michael@0: // succeeds (it does an NtOpenFile) but windows\system32\notepad.exe is michael@0: // denied since there is no wild card in the rule. michael@0: EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"")); michael@0: runner.SetTestState(BEFORE_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); michael@0: michael@0: runner.SetTestState(AFTER_REVERT); michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace")); michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe")); michael@0: } michael@0: michael@0: // http://crbug.com/146944 michael@0: TEST(FilePolicyTest, DISABLED_TestReparsePoint) { michael@0: TestRunner runner; michael@0: michael@0: // Create a temp file because we need write access to it. michael@0: wchar_t temp_directory[MAX_PATH]; michael@0: wchar_t temp_file_name[MAX_PATH]; michael@0: ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); michael@0: ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); michael@0: michael@0: // Delete the file and create a directory instead. michael@0: ASSERT_TRUE(::DeleteFile(temp_file_name)); michael@0: ASSERT_TRUE(::CreateDirectory(temp_file_name, NULL)); michael@0: michael@0: // Create a temporary file in the subfolder. michael@0: std::wstring subfolder = temp_file_name; michael@0: std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1); michael@0: std::wstring temp_file = subfolder + L"\\file_" + temp_file_title; michael@0: michael@0: HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS, michael@0: FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, michael@0: CREATE_ALWAYS, 0, NULL); michael@0: ASSERT_TRUE(INVALID_HANDLE_VALUE != file); michael@0: ASSERT_TRUE(::CloseHandle(file)); michael@0: michael@0: // Create a temporary file in the temp directory. michael@0: std::wstring temp_dir = temp_directory; michael@0: std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title; michael@0: file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS, michael@0: FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, michael@0: CREATE_ALWAYS, 0, NULL); michael@0: ASSERT_TRUE(file != NULL); michael@0: ASSERT_TRUE(::CloseHandle(file)); michael@0: michael@0: // Give write access to the temp directory. michael@0: std::wstring temp_dir_wildcard = temp_dir + L"*"; michael@0: EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, michael@0: temp_dir_wildcard.c_str())); michael@0: michael@0: // Prepare the command to execute. michael@0: std::wstring command_write; michael@0: command_write += L"File_Create Write \""; michael@0: command_write += temp_file; michael@0: command_write += L"\""; michael@0: michael@0: // Verify that we have write access to the original file michael@0: EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str())); michael@0: michael@0: // Replace the subfolder by a reparse point to %temp%. michael@0: ::DeleteFile(temp_file.c_str()); michael@0: HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, michael@0: FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, michael@0: OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); michael@0: EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); michael@0: michael@0: std::wstring temp_dir_nt; michael@0: temp_dir_nt += L"\\??\\"; michael@0: temp_dir_nt += temp_dir; michael@0: EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); michael@0: EXPECT_TRUE(::CloseHandle(dir)); michael@0: michael@0: // Try to open the file again. michael@0: EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str())); michael@0: michael@0: // Remove the reparse point. michael@0: dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, michael@0: FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, michael@0: FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, michael@0: NULL); michael@0: EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); michael@0: EXPECT_TRUE(DeleteReparsePoint(dir)); michael@0: EXPECT_TRUE(::CloseHandle(dir)); michael@0: michael@0: // Cleanup. michael@0: EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str())); michael@0: EXPECT_TRUE(::RemoveDirectory(subfolder.c_str())); michael@0: } michael@0: michael@0: } // namespace sandbox