# HG changeset patch # User Bob Owen # Date 1454317140 0 # Mon Feb 01 08:59:00 2016 +0000 # Node ID 9870a92ea5f352ab5a841003a30ab52c8deb589e # Parent d62b6a3a0c58528a8bf864bb5ab6bb9faada972b Change to allow network drives in sandbox rules with non-file device fix. r=aklotz Originally landed in changeset: https://hg.mozilla.org/mozilla-central/rev/c70d06fa5302 diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.cc b/security/sandbox/chromium/sandbox/win/src/win_utils.cc --- a/security/sandbox/chromium/sandbox/win/src/win_utils.cc +++ b/security/sandbox/chromium/sandbox/win/src/win_utils.cc @@ -194,61 +194,66 @@ bool ResolveRegistryName(std::wstring na return false; } // |full_path| can have any of the following forms: // \??\c:\some\foo\bar // \Device\HarddiskVolume0\some\foo\bar // \??\HarddiskVolume0\some\foo\bar +// \??\UNC\SERVER\Share\some\foo\bar DWORD IsReparsePoint(const std::wstring& full_path) { // Check if it's a pipe. We can't query the attributes of a pipe. if (IsPipe(full_path)) return ERROR_NOT_A_REPARSE_POINT; std::wstring 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 = std::wstring(kNTDotPrefix) + path; - added_implied_device = true; + // Add Win32 device namespace prefix, required for some Windows APIs. + path.insert(0, kNTDotPrefix); } - std::wstring::size_type last_pos = std::wstring::npos; - bool passed_once = false; + // Ensure that volume path matches start of path. + wchar_t vol_path[MAX_PATH]; + if (!::GetVolumePathNameW(path.c_str(), vol_path, MAX_PATH)) { + // This will fail if this is a device that isn't volume related, which can't + // then be a reparse point. + return is_device_path ? ERROR_NOT_A_REPARSE_POINT : ERROR_INVALID_NAME; + } + + // vol_path includes a trailing slash, so reduce size for path and loop check. + size_t vol_path_len = wcslen(vol_path) - 1; + if (!EqualPath(path, vol_path, vol_path_len)) { + return ERROR_INVALID_NAME; + } do { - path = path.substr(0, last_pos); - DWORD attributes = ::GetFileAttributes(path.c_str()); if (INVALID_FILE_ATTRIBUTES == attributes) { DWORD error = ::GetLastError(); if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND && + error != ERROR_INVALID_FUNCTION && error != ERROR_INVALID_NAME) { // Unexpected error. - if (passed_once && added_implied_device && - (path.rfind(L'\\') == kNTDotPrefixLen - 1)) { - break; - } return error; } } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) { // This is a reparse point. return ERROR_SUCCESS; } - passed_once = true; - last_pos = path.rfind(L'\\'); - } while (last_pos > 2); // Skip root dir. + path.resize(path.rfind(L'\\')); + } while (path.size() > vol_path_len); // Skip root dir. return ERROR_NOT_A_REPARSE_POINT; } // 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) { // Check if it's a pipe. @@ -258,63 +263,67 @@ bool SameObject(HANDLE handle, const wch std::wstring actual_path; if (!GetPathFromHandle(handle, &actual_path)) return false; std::wstring path(full_path); DCHECK_NT(!path.empty()); // This may end with a backslash. - const wchar_t kBackslash = '\\'; - if (path.back() == kBackslash) - path = path.substr(0, path.length() - 1); + if (path.back() == L'\\') { + path.pop_back(); + } - // Perfect match (case-insesitive check). + // Perfect match (case-insensitive check). if (EqualPath(actual_path, path)) return true; bool nt_path = IsNTPath(path, &path); bool has_drive = StartsWithDriveLetter(path); if (!has_drive && nt_path) { std::wstring simple_actual_path; - if (!IsDevicePath(actual_path, &simple_actual_path)) - return false; - - // Perfect match (case-insesitive check). - return (EqualPath(simple_actual_path, path)); + if (IsDevicePath(path, &path)) { + if (IsDevicePath(actual_path, &simple_actual_path)) { + // Perfect match (case-insensitive check). + return (EqualPath(simple_actual_path, path)); + } else { + return false; + } + } else { + // Add Win32 device namespace for GetVolumePathName. + path.insert(0, kNTDotPrefix); + } } - if (!has_drive) + // Get the volume path in the same format as actual_path. + wchar_t vol_path[MAX_PATH]; + if (!::GetVolumePathName(path.c_str(), vol_path, MAX_PATH)) { 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[0], 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) + } + size_t vol_path_len = wcslen(vol_path); + base::string16 nt_vol; + if (!GetNtPathFromWin32Path(vol_path, &nt_vol)) { return false; - - // Ignore the nulls at the end. - vol_length = static_cast(wcslen(vol_name)); + } // The two paths should be the same length. - if (vol_length + path.size() - 2 != actual_path.size()) + if (nt_vol.size() + path.size() - vol_path_len != actual_path.size()) { return false; + } - // Check up to the drive letter. - if (!EqualPath(actual_path, vol_name, vol_length)) + // Check the volume matches. + if (!EqualPath(actual_path, nt_vol.c_str(), nt_vol.size())) { return false; + } - // Check the path after the drive letter. - if (!EqualPath(actual_path, vol_length, path, 2)) + // Check the path after the volume matches. + if (!EqualPath(actual_path, nt_vol.size(), path, vol_path_len)) { return false; + } return true; } // Just make a best effort here. There are lots of corner cases that we're // not expecting - and will fail to make long. bool ConvertToLongPath(std::wstring* native_path, const std::wstring* drive_letter) {