diff options
-rw-r--r-- | sandbox/win/src/file_policy_test.cc | 34 | ||||
-rw-r--r-- | sandbox/win/src/filesystem_policy.cc | 21 | ||||
-rw-r--r-- | sandbox/win/src/win_utils.cc | 172 | ||||
-rw-r--r-- | sandbox/win/src/win_utils.h | 4 |
4 files changed, 185 insertions, 46 deletions
diff --git a/sandbox/win/src/file_policy_test.cc b/sandbox/win/src/file_policy_test.cc index 199dd4c..8b52362 100644 --- a/sandbox/win/src/file_policy_test.cc +++ b/sandbox/win/src/file_policy_test.cc @@ -9,6 +9,7 @@ #include <winioctl.h> #include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" #include "sandbox/win/src/filesystem_policy.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/sandbox.h" @@ -109,7 +110,7 @@ SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; base::string16 file(argv[0]); - if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) + if (0 != _wcsnicmp(file.c_str(), kNTDevicePrefix, kNTDevicePrefixLen)) file = MakePathToSys(argv[0], true); UNICODE_STRING object_name; @@ -279,6 +280,9 @@ TEST(FilePolicyTest, AllowNtCreateCalc) { } TEST(FilePolicyTest, AllowNtCreateWithNativePath) { + if (base::win::GetVersion() < base::win::VERSION_WIN7) + return; + base::string16 calc = MakePathToSys(L"calc.exe", false); base::string16 nt_path; ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path)); @@ -330,6 +334,34 @@ TEST(FilePolicyTest, AllowReadOnly) { DeleteFile(temp_file_name); } +// Tests support of "\\\\.\\DeviceName" kind of paths. +TEST(FilePolicyTest, AllowImplicitDeviceName) { + if (base::win::GetVersion() < base::win::VERSION_WIN7) + return; + + TestRunner runner; + + wchar_t temp_directory[MAX_PATH]; + wchar_t temp_file_name[MAX_PATH]; + ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); + + std::wstring path; + EXPECT_TRUE(ConvertToLongPath(temp_file_name, &path)); + EXPECT_TRUE(GetNtPathFromWin32Path(path, &path)); + path = path.substr(sandbox::kNTDevicePrefixLen); + + wchar_t command[MAX_PATH + 20] = {0}; + wsprintf(command, L"File_Create Read \"\\\\.\\%ls\"", path.c_str()); + path = std::wstring(kNTPrefix) + path; + + EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command)); + EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, path.c_str())); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command)); + + DeleteFile(temp_file_name); +} + TEST(FilePolicyTest, AllowWildcard) { TestRunner runner; diff --git a/sandbox/win/src/filesystem_policy.cc b/sandbox/win/src/filesystem_policy.cc index 1ce21c8..0349ff3 100644 --- a/sandbox/win/src/filesystem_policy.cc +++ b/sandbox/win/src/filesystem_policy.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" #include "sandbox/win/src/ipc_tags.h" #include "sandbox/win/src/policy_engine_opcodes.h" #include "sandbox/win/src/policy_params.h" @@ -78,18 +79,16 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, return false; } - // Don't do any pre-processing if the name starts like the the native - // object manager style. - if (0 != _wcsnicmp(mod_name.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) { - // 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)) { - // The path to be added might contain a reparse point. - NOTREACHED(); - return false; - } + if (!PreProcessName(mod_name, &mod_name)) { + // The path to be added might contain a reparse point. + NOTREACHED(); + return false; + } + // 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 (_wcsnicmp(mod_name.c_str(), kNTDevicePrefix, kNTDevicePrefixLen)) { mod_name = FixNTPrefixForMatch(mod_name); name = mod_name.c_str(); } diff --git a/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc index 12f8189..d2b507d 100644 --- a/sandbox/win/src/win_utils.cc +++ b/sandbox/win/src/win_utils.cc @@ -34,19 +34,89 @@ const KnownReservedKey kKnownKey[] = { { L"HKEY_DYN_DATA", HKEY_DYN_DATA} }; +// These functions perform case independent path comparisons. +bool EqualPath(const base::string16& first, const base::string16& second) { + return _wcsicmp(first.c_str(), second.c_str()) == 0; +} + +bool EqualPath(const base::string16& first, size_t first_offset, + const base::string16& second, size_t second_offset) { + return _wcsicmp(first.c_str() + first_offset, + second.c_str() + second_offset) == 0; +} + +bool EqualPath(const base::string16& first, + const wchar_t* second, size_t second_len) { + return _wcsnicmp(first.c_str(), second, second_len) == 0; +} + +bool EqualPath(const base::string16& first, size_t first_offset, + const wchar_t* second, size_t second_len) { + return _wcsnicmp(first.c_str() + first_offset, second, second_len) == 0; +} + +// Returns true if |path| starts with "\??\" and returns a path without that +// component. +bool IsNTPath(const base::string16& path, base::string16* trimmed_path ) { + if ((path.size() < sandbox::kNTPrefixLen) || + (0 != path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))) { + *trimmed_path = path; + return false; + } + + *trimmed_path = path.substr(sandbox::kNTPrefixLen); + return true; +} + +// Returns true if |path| starts with "\Device\" and returns a path without that +// component. +bool IsDevicePath(const base::string16& path, base::string16* trimmed_path ) { + if ((path.size() < sandbox::kNTDevicePrefixLen) || + (!EqualPath(path, sandbox::kNTDevicePrefix, + sandbox::kNTDevicePrefixLen))) { + *trimmed_path = path; + return false; + } + + *trimmed_path = path.substr(sandbox::kNTDevicePrefixLen); + return true; +} + +bool StartsWithDriveLetter(const base::string16& path) { + if (path.size() < 3) + return false; + + if (path[1] != L':' || path[2] != L'\\') + return false; + + return (path[0] >= 'a' && path[0] <= 'z') || + (path[0] >= 'A' && path[0] <= 'Z'); +} + +const wchar_t kNTDotPrefix[] = L"\\\\.\\"; +const size_t kNTDotPrefixLen = arraysize(kNTDotPrefix) - 1; + +// Removes "\\\\.\\" from the path. +void RemoveImpliedDevice(base::string16* path) { + if (0 == path->compare(0, kNTDotPrefixLen, kNTDotPrefix)) + *path = path->substr(kNTDotPrefixLen); +} + } // namespace namespace sandbox { // Returns true if the provided path points to a pipe. bool IsPipe(const base::string16& path) { - base::string16 lower_path = base::StringToLowerASCII(path); size_t start = 0; - if (0 == lower_path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix)) + if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix)) start = sandbox::kNTPrefixLen; const wchar_t kPipe[] = L"pipe\\"; - return (0 == lower_path.compare(start, arraysize(kPipe) - 1, kPipe)); + if (path.size() < start + arraysize(kPipe) - 1) + return false; + + return EqualPath(path, start, kPipe, arraysize(kPipe) - 1); } HKEY GetReservedKeyFromName(const base::string16& name) { @@ -82,20 +152,33 @@ bool ResolveRegistryName(base::string16 name, base::string16* resolved_name) { return false; } +// |full_path| can have any of the following forms: +// \??\c:\some\foo\bar +// \Device\HarddiskVolume0\some\foo\bar +// \??\HarddiskVolume0\some\foo\bar DWORD IsReparsePoint(const base::string16& full_path, bool* result) { - base::string16 path = full_path; - - // Remove the nt prefix. - if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) - path = path.substr(kNTPrefixLen); - // Check if it's a pipe. We can't query the attributes of a pipe. - if (IsPipe(path)) { + if (IsPipe(full_path)) { *result = FALSE; return ERROR_SUCCESS; } + base::string16 path; + bool nt_path = IsNTPath(full_path, &path); + bool has_drive = StartsWithDriveLetter(path); + bool is_device_path = IsDevicePath(path, &path); + + if (!has_drive && !is_device_path && !nt_path) + return ERROR_INVALID_NAME; + + bool added_implied_device = false; + if (!has_drive) { + path = base::string16(kNTDotPrefix) + path; + added_implied_device = true; + } + base::string16::size_type last_pos = base::string16::npos; + bool passed_once = false; do { path = path.substr(0, last_pos); @@ -107,6 +190,10 @@ DWORD IsReparsePoint(const base::string16& full_path, bool* result) { error != ERROR_PATH_NOT_FOUND && error != ERROR_INVALID_NAME) { // Unexpected error. + if (passed_once && added_implied_device && + (path.rfind(L'\\') == kNTDotPrefixLen - 1)) { + break; + } NOTREACHED_NT(); return error; } @@ -116,6 +203,7 @@ DWORD IsReparsePoint(const base::string16& full_path, bool* result) { return ERROR_SUCCESS; } + passed_once = true; last_pos = path.rfind(L'\\'); } while (last_pos > 2); // Skip root dir. @@ -123,42 +211,48 @@ DWORD IsReparsePoint(const base::string16& full_path, bool* result) { return ERROR_SUCCESS; } -// We get a |full_path| of the form \??\c:\some\foo\bar, and the name that +// We get a |full_path| of the forms accepted by IsReparsePoint(), and the name // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar. bool SameObject(HANDLE handle, const wchar_t* full_path) { - base::string16 path(full_path); - DCHECK_NT(!path.empty()); - // Check if it's a pipe. - if (IsPipe(path)) + if (IsPipe(full_path)) return true; base::string16 actual_path; if (!GetPathFromHandle(handle, &actual_path)) return false; + base::string16 path(full_path); + DCHECK_NT(!path.empty()); + // This may end with a backslash. const wchar_t kBackslash = '\\'; if (path[path.length() - 1] == kBackslash) path = path.substr(0, path.length() - 1); // Perfect match (case-insesitive check). - if (0 == _wcsicmp(actual_path.c_str(), path.c_str())) + if (EqualPath(actual_path, path)) return true; - // Look for the drive letter. - size_t colon_pos = path.find(L':'); - if (colon_pos == 0 || colon_pos == base::string16::npos) - return false; + bool nt_path = IsNTPath(path, &path); + bool has_drive = StartsWithDriveLetter(path); + + if (!has_drive && nt_path) { + base::string16 simple_actual_path; + if (!IsDevicePath(actual_path, &simple_actual_path)) + return false; + + // Perfect match (case-insesitive check). + return (EqualPath(simple_actual_path, path)); + } - // Only one character for the drive. - if (colon_pos > 1 && path[colon_pos - 2] != kBackslash) + if (!has_drive) 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)); + memcpy(drive, &path[0], 2 * sizeof(*drive)); // We'll get a double null terminated string. DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH); @@ -169,28 +263,39 @@ bool SameObject(HANDLE handle, const wchar_t* full_path) { vol_length = static_cast<DWORD>(wcslen(vol_name)); // The two paths should be the same length. - if (vol_length + path.size() - (colon_pos + 1) != actual_path.size()) + if (vol_length + path.size() - 2 != actual_path.size()) return false; // Check up to the drive letter. - if (0 != _wcsnicmp(actual_path.c_str(), vol_name, vol_length)) + if (!EqualPath(actual_path, vol_name, vol_length)) return false; // Check the path after the drive letter. - if (0 != _wcsicmp(&actual_path[vol_length], &path[colon_pos + 1])) + if (!EqualPath(actual_path, vol_length, path, 2)) return false; return true; } +// Paths like \Device\HarddiskVolume0\some\foo\bar are assumed to be already +// expanded. bool ConvertToLongPath(const base::string16& short_path, base::string16* long_path) { - // Check if the path is a NT path. - bool is_nt_path = false; - base::string16 path = short_path; - if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) { - path = path.substr(kNTPrefixLen); - is_nt_path = true; + if (IsPipe(short_path)) { + // TODO(rvargas): Change the signature to use a single argument. + long_path->assign(short_path); + return true; + } + + base::string16 path; + if (IsDevicePath(short_path, &path)) + return false; + + bool is_nt_path = IsNTPath(path, &path); + bool added_implied_device = false; + if (!StartsWithDriveLetter(path) && is_nt_path) { + path = base::string16(kNTDotPrefix) + path; + added_implied_device = true; } DWORD size = MAX_PATH; @@ -226,6 +331,9 @@ bool ConvertToLongPath(const base::string16& short_path, } if (return_value != 0) { + if (added_implied_device) + RemoveImpliedDevice(&path); + if (is_nt_path) { *long_path = kNTPrefix; *long_path += path; diff --git a/sandbox/win/src/win_utils.h b/sandbox/win/src/win_utils.h index f576492..3e4565f 100644 --- a/sandbox/win/src/win_utils.h +++ b/sandbox/win/src/win_utils.h @@ -17,8 +17,8 @@ namespace sandbox { const wchar_t kNTPrefix[] = L"\\??\\"; const size_t kNTPrefixLen = arraysize(kNTPrefix) - 1; -const wchar_t kNTObjManPrefix[] = L"\\Device\\"; -const size_t kNTObjManPrefixLen = arraysize(kNTObjManPrefix) - 1; +const wchar_t kNTDevicePrefix[] = L"\\Device\\"; +const size_t kNTDevicePrefixLen = arraysize(kNTDevicePrefix) - 1; // Automatically acquires and releases a lock when the object is // is destroyed. |