diff options
author | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-27 19:20:42 +0000 |
---|---|---|
committer | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-27 19:20:42 +0000 |
commit | 4f1f3d0f03c79ddaace56f067cf28a27f9466b7d (patch) | |
tree | bc0bcae7b48b6e4e218d4fca358af50467893940 /sandbox/src | |
parent | 2377f7f26715ae20f671c5fd7e7edee778c1f64f (diff) | |
download | chromium_src-4f1f3d0f03c79ddaace56f067cf28a27f9466b7d.zip chromium_src-4f1f3d0f03c79ddaace56f067cf28a27f9466b7d.tar.gz chromium_src-4f1f3d0f03c79ddaace56f067cf28a27f9466b7d.tar.bz2 |
Improve handling and testing of reparse points.
BUG=28804
TEST=unit tests.
Review URL: http://codereview.chromium.org/553080
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37286 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox/src')
-rw-r--r-- | sandbox/src/file_policy_test.cc | 89 | ||||
-rw-r--r-- | sandbox/src/filesystem_policy.cc | 12 | ||||
-rw-r--r-- | sandbox/src/win_utils.cc | 80 | ||||
-rw-r--r-- | sandbox/src/win_utils.h | 7 | ||||
-rw-r--r-- | sandbox/src/win_utils_unittest.cc | 51 |
5 files changed, 150 insertions, 89 deletions
diff --git a/sandbox/src/file_policy_test.cc b/sandbox/src/file_policy_test.cc index e62f08a..f4a2e19 100644 --- a/sandbox/src/file_policy_test.cc +++ b/sandbox/src/file_policy_test.cc @@ -1,92 +1,23 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "sandbox/src/sandbox_policy.h" - #include <windows.h> #include <winioctl.h> #include "base/scoped_handle_win.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "sandbox/src/nt_internals.h" #include "sandbox/src/sandbox.h" #include "sandbox/src/sandbox_factory.h" -#include "sandbox/src/nt_internals.h" +#include "sandbox/src/sandbox_policy.h" #include "sandbox/tests/common/controller.h" +#include "sandbox/tests/common/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" #define BINDNTDLL(name) \ name ## Function name = reinterpret_cast<name ## Function>( \ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) -namespace { - -typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - }; -} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; - -// Sets a reparse point. |source| will now point to |target|. Returns true if -// the call succeeds, false otherwise. -bool SetReparsePoint(HANDLE source, const wchar_t* target) { - USHORT size_target = static_cast<USHORT>(wcslen(target)) * sizeof(target[0]); - - char buffer[2000] = {0}; - DWORD returned; - - REPARSE_DATA_BUFFER* data = reinterpret_cast<REPARSE_DATA_BUFFER*>(buffer); - - data->ReparseTag = 0xa0000003; - memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2); - data->MountPointReparseBuffer.SubstituteNameLength = size_target; - data->MountPointReparseBuffer.PrintNameOffset = size_target + 2; - data->ReparseDataLength = size_target + 4 + 8; - - int data_size = data->ReparseDataLength + 8; - - if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size, - NULL, 0, &returned, NULL)) { - return false; - } - return true; -} - -// Delete the reparse point referenced by |source|. Returns true if the call -// succeeds, false otherwise. -bool DeleteReparsePoint(HANDLE source) { - DWORD returned; - REPARSE_DATA_BUFFER data = {0}; - data.ReparseTag = 0xa0000003; - if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0, - &returned, NULL)) { - return false; - } - - return true; -} - -} // unamed namespace - namespace sandbox { const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE; @@ -126,7 +57,7 @@ SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } - std::wstring full_path = MakePathToSys32(argv[0], false); + std::wstring full_path = MakePathToSys(argv[0], false); if (full_path.empty()) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } @@ -158,7 +89,7 @@ SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { if (argc != 1) return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - std::wstring file = MakePathToSys32(argv[0], true); + std::wstring file = MakePathToSys(argv[0], true); UNICODE_STRING object_name; RtlInitUnicodeString(&object_name, file.c_str()); @@ -193,7 +124,7 @@ SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { if (argc != 1) return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - std::wstring file = MakePathToSys32(argv[0], true); + std::wstring file = MakePathToSys(argv[0], true); UNICODE_STRING object_name; RtlInitUnicodeString(&object_name, file.c_str()); @@ -217,7 +148,7 @@ SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { } SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { - std::wstring sys_path = MakePathToSys32(L"", false); + std::wstring sys_path = MakePathToSys(L"", false); if (sys_path.empty()) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } @@ -273,7 +204,7 @@ SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t **argv) { bool expect_directory = (L'd' == argv[1][0]); UNICODE_STRING object_name; - std::wstring file = MakePathToSys32(argv[0], true); + std::wstring file = MakePathToSys(argv[0], true); RtlInitUnicodeString(&object_name, file.c_str()); OBJECT_ATTRIBUTES obj_attributes = {0}; diff --git a/sandbox/src/filesystem_policy.cc b/sandbox/src/filesystem_policy.cc index c11c031..a703188 100644 --- a/sandbox/src/filesystem_policy.cc +++ b/sandbox/src/filesystem_policy.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -40,6 +40,12 @@ NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle, return status; } + if (!sandbox::SameObject(local_handle, obj_attributes->ObjectName->Buffer)) { + // The handle points somewhere else. Fail the operation. + ::CloseHandle(local_handle); + return STATUS_ACCESS_DENIED; + } + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process, target_file_handle, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { @@ -49,7 +55,7 @@ NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle, return STATUS_SUCCESS; } -} +} // namespace. namespace sandbox { @@ -61,7 +67,7 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, return false; } - // TODO(cpu): This prefix add is a hack because we don't have the + // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the // infrastructure to normalize names. In any case we need to escape the // question marks. if (!PreProcessName(mod_name, &mod_name)) { diff --git a/sandbox/src/win_utils.cc b/sandbox/src/win_utils.cc index f5d39f2..bf4936b 100644 --- a/sandbox/src/win_utils.cc +++ b/sandbox/src/win_utils.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -32,6 +32,16 @@ const KnownReservedKey kKnownKey[] = { { L"HKEY_DYN_DATA", HKEY_DYN_DATA} }; +// Returns true if the provided path points to a pipe. +bool IsPipe(const std::wstring& path) { + size_t start = 0; + if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix)) + start = sandbox::kNTPrefixLen; + + const wchar_t kPipe[] = L"pipe\\"; + return (0 == path.compare(start, arraysize(kPipe) - 1, kPipe)); +} + } // namespace namespace sandbox { @@ -77,8 +87,7 @@ DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { path = path.substr(kNTPrefixLen); // Check if it's a pipe. We can't query the attributes of a pipe. - const wchar_t kPipe[] = L"pipe\\"; - if (0 == path.compare(0, arraysize(kPipe) - 1, kPipe)) { + if (IsPipe(path)) { *result = FALSE; return ERROR_SUCCESS; } @@ -111,6 +120,66 @@ DWORD IsReparsePoint(const std::wstring& full_path, bool* result) { return ERROR_SUCCESS; } +// We get a |full_path| of the form \??\c:\some\foo\bar, and the name that +// we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar. +bool SameObject(HANDLE handle, const wchar_t* full_path) { + std::wstring path(full_path); + DCHECK(!path.empty()); + + // Check if it's a pipe. + if (IsPipe(path)) + return true; + + std::wstring actual_path; + if (!GetPathFromHandle(handle, &actual_path)) + return false; + + // This may end with a backslash. + const wchar_t kBackslash = '\\'; + if (path[path.length() - 1] == kBackslash) + path = path.substr(0, path.length() - 1); + + if (0 == actual_path.compare(full_path)) + return true; + + // Look for the drive letter. + size_t colon_pos = path.find(L':'); + if (colon_pos == 0 || colon_pos == std::wstring::npos) + return false; + + // Only one character for the drive. + if (colon_pos > 1 && path[colon_pos - 2] != kBackslash) + return false; + + // We only need 3 chars, but let's alloc a buffer for four. + wchar_t drive[4] = {0}; + wchar_t vol_name[MAX_PATH]; + memcpy(drive, &path[colon_pos - 1], 2 * sizeof(*drive)); + + // We'll get a double null terminated string. + DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH); + if (vol_length < 2 || vol_length == MAX_PATH) + return false; + + // Ignore the nulls at the end. + vol_length -= 2; + + // The two paths should be the same length. + if (vol_length + path.size() - (colon_pos + 1) != actual_path.size()) + return false; + + // Check up to the drive letter. + if (0 != actual_path.compare(0, vol_length, vol_name)) + return false; + + // Check the path after the drive letter. + if (0 != actual_path.compare(vol_length, std::wstring::npos, + &path[colon_pos + 1])) + return false; + + return true; +} + bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path) { // Check if the path is a NT path. @@ -171,8 +240,9 @@ bool GetPathFromHandle(HANDLE handle, std::wstring* path) { NtQueryObjectFunction NtQueryObject = NULL; ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); - OBJECT_NAME_INFORMATION* name = NULL; - ULONG size = 0; + OBJECT_NAME_INFORMATION initial_buffer; + OBJECT_NAME_INFORMATION* name = &initial_buffer; + ULONG size = sizeof(initial_buffer); // Query the name information a first time to get the size of the name. NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size, &size); diff --git a/sandbox/src/win_utils.h b/sandbox/src/win_utils.h index b3aad3f..c52f3e3 100644 --- a/sandbox/src/win_utils.h +++ b/sandbox/src/win_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -30,8 +30,8 @@ class AutoLock { }; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(AutoLock); CRITICAL_SECTION *lock_; + DISALLOW_IMPLICIT_CONSTRUCTORS(AutoLock); }; // Basic implementation of a singleton which calls the destructor @@ -71,6 +71,9 @@ bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path); // returns true if any of them is a reparse point. DWORD IsReparsePoint(const std::wstring& full_path, bool* result); +// Returns true if the handle corresponds to the object pointed by this path. +bool SameObject(HANDLE handle, const wchar_t* full_path); + // Resolves a handle to a path. Returns true if the handle can be resolved. bool GetPathFromHandle(HANDLE handle, std::wstring* path); diff --git a/sandbox/src/win_utils_unittest.cc b/sandbox/src/win_utils_unittest.cc new file mode 100644 index 0000000..a7dc998 --- /dev/null +++ b/sandbox/src/win_utils_unittest.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +#include "sandbox/src/win_utils.h" +#include "sandbox/tests/common/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(WinUtils, IsReparsePoint) { + using sandbox::IsReparsePoint; + + // Create a temp file because we need write access to it. + wchar_t temp_directory[MAX_PATH]; + wchar_t my_folder[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0); + + // Delete the file and create a directory instead. + ASSERT_TRUE(::DeleteFile(my_folder)); + ASSERT_TRUE(::CreateDirectory(my_folder, NULL)); + + bool result = true; + EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(my_folder, &result)); + EXPECT_FALSE(result); + + // We have to fix Bug 32224 to pass this test. + std::wstring not_found = std::wstring(my_folder) + L"\\foo\\bar"; + // EXPECT_EQ(ERROR_PATH_NOT_FOUND, IsReparsePoint(not_found, &result)); + + std::wstring new_file = std::wstring(my_folder) + L"\\foo"; + EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); + EXPECT_FALSE(result); + + // Replace the directory with a reparse point to %temp%. + HANDLE dir = ::CreateFile(my_folder, FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); + + std::wstring temp_dir_nt = std::wstring(L"\\??\\") + temp_directory; + EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); + + EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); + EXPECT_TRUE(result); + + EXPECT_TRUE(DeleteReparsePoint(dir)); + EXPECT_TRUE(::CloseHandle(dir)); + EXPECT_TRUE(::RemoveDirectory(my_folder)); +} |