Bug 1634267 - DOS device paths shouldn't be blocked by IsBlockedUNCPath; r=dom-workers-and-storage-reviewers,janv,froydnj

Bug 1536796 introduces "\\?\" prefix (DOS device specifier) to Windows file
paths. At the moment, the prefix is only prepended to the file paths that start
with a disk designator and a backslash.

On the other hands, IsBlockedUNCPath blocks file paths that start with "\\" in
Windows and that includes DOS device paths (the cases mentioned above).

Thus, this patch prevents DOS device paths from being treating as UNC paths in
IsBlockedUNCPath.

Differential Revision: https://phabricator.services.mozilla.com/D73621
This commit is contained in:
Tom Tung 2020-05-13 14:32:35 +00:00
Родитель e120487bbb
Коммит 3d101cac5a
4 изменённых файлов: 124 добавлений и 19 удалений

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Tokenizer.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
@ -33,6 +34,8 @@ static WinPaths& PathWhitelist() {
}
#ifdef XP_WIN
const auto kDevicePathSpecifier = NS_LITERAL_STRING("\\\\?\\");
typedef char16_t char_path_t;
#else
typedef char char_path_t;
@ -223,6 +226,28 @@ class TNormalizer : public TTokenizer<TChar> {
nsTArray<nsTDependentSubstring<TChar>> mStack;
};
#ifdef XP_WIN
bool IsDOSDevicePathWithDrive(const nsAString& aFilePath) {
if (!StringBeginsWith(aFilePath, kDevicePathSpecifier)) {
return false;
}
const auto pathNoPrefix =
nsDependentSubstring(aFilePath, kDevicePathSpecifier.Length());
// After the device path specifier, the rest of file path can be:
// - starts with the volume or drive. e.g. \\?\C:\...
// - UNCs. e.g. \\?\UNC\Server\Share\Test\Foo.txt
// - device UNCs. e.g. \\?\server1\e:\utilities\\filecomparer\...
// The first case should not be blocked by IsBlockedUNCPath.
if (!StartsWithDiskDesignatorAndBackslash(pathNoPrefix)) {
return false;
}
return true;
}
#endif
} // namespace
bool IsBlockedUNCPath(const nsAString& aFilePath) {
@ -235,6 +260,15 @@ bool IsBlockedUNCPath(const nsAString& aFilePath) {
return false;
}
#ifdef XP_WIN
// ToDo: We don't need to check this once we can check if there is a valid
// server or host name that is prefaced by "\\".
// https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
if (IsDOSDevicePathWithDrive(aFilePath)) {
return false;
}
#endif
nsAutoString normalized;
if (!Normalizer(aFilePath, Normalizer::Token::Char('\\')).Get(normalized)) {
// Broken paths are considered invalid and thus inaccessible
@ -309,6 +343,18 @@ bool IsAllowedPath(const nsTSubstring<char_path_t>& aFilePath) {
return true;
}
#ifdef XP_WIN
bool StartsWithDiskDesignatorAndBackslash(const nsAString& aAbsolutePath) {
// aAbsolutePath can only be (in regular expression):
// UNC path: ^\\\\.*
// A single backslash: ^\\.*
// A disk designator with a backslash: ^[A-Za-z]:\\.*
return aAbsolutePath.Length() >= 3 && IsAsciiAlpha(aAbsolutePath.CharAt(0)) &&
aAbsolutePath.CharAt(1) == L':' &&
aAbsolutePath.CharAt(2) == kPathSeparator;
}
#endif
void testing::SetBlockUNCPaths(bool aBlock) { sBlockUNCPaths = aBlock; }
void testing::AddDirectoryToWhitelist(nsAString const& aPath) {

Просмотреть файл

@ -19,7 +19,14 @@ bool IsAllowedPath(const nsAString& aFilePath);
bool IsAllowedPath(const nsACString& aFilePath);
#endif
#ifdef XP_WIN
bool StartsWithDiskDesignatorAndBackslash(const nsAString& aAbsolutePath);
#endif
extern const char kPathSeparator;
#ifdef XP_WIN
extern const nsLiteralString kDevicePathSpecifier;
#endif
namespace testing {

Просмотреть файл

@ -6,7 +6,6 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/TextUtils.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/Utf8.h"
@ -60,6 +59,7 @@
#include "mozilla/WidgetUtils.h"
using namespace mozilla;
using mozilla::FilePreferences::kDevicePathSpecifier;
using mozilla::FilePreferences::kPathSeparator;
#define CHECK_mWorkingPath() \
@ -80,20 +80,8 @@ using mozilla::FilePreferences::kPathSeparator;
# define DRIVE_REMOTE 4
#endif
constexpr auto kDevicePathSpecifier = NS_LITERAL_STRING("\\\\?\\");
namespace {
bool StartsWithDiskDesignatorAndBackslash(const nsAString& aAbsolutePath) {
// aAbsolutePath can only be (in regular expression):
// UNC path: ^\\\\.*
// A single backslash: ^\\.*
// A disk designator with a backslash: ^[A-Za-z]:\\.*
return aAbsolutePath.Length() >= 3 && IsAsciiAlpha(aAbsolutePath.CharAt(0)) &&
aAbsolutePath.CharAt(1) == L':' &&
aAbsolutePath.CharAt(2) == kPathSeparator;
}
nsresult NewLocalFile(const nsAString& aPath, bool aFollowLinks,
bool aUseDOSDevicePathSyntax, nsIFile** aResult) {
RefPtr<nsLocalFile> file = new nsLocalFile();
@ -990,7 +978,7 @@ nsLocalFile::InitWithPath(const nsAString& aFilePath) {
// Prepend the "\\?\" prefix if the useDOSDevicePathSyntax is set and the path
// starts with a disk designator and backslash.
if (mUseDOSDevicePathSyntax &&
StartsWithDiskDesignatorAndBackslash(mWorkingPath)) {
FilePreferences::StartsWithDiskDesignatorAndBackslash(mWorkingPath)) {
mWorkingPath = kDevicePathSpecifier + mWorkingPath;
}
@ -1525,8 +1513,9 @@ nsLocalFile::SetLeafName(const nsAString& aLeafName) {
NS_IMETHODIMP
nsLocalFile::GetPath(nsAString& aResult) {
MOZ_ASSERT_IF(mUseDOSDevicePathSyntax,
!StartsWithDiskDesignatorAndBackslash(mWorkingPath));
MOZ_ASSERT_IF(
mUseDOSDevicePathSyntax,
!FilePreferences::StartsWithDiskDesignatorAndBackslash(mWorkingPath));
aResult = mWorkingPath;
return NS_OK;
}
@ -2952,8 +2941,9 @@ nsLocalFile::GetTarget(nsAString& aResult) {
aResult.Truncate();
Resolve();
MOZ_ASSERT_IF(mUseDOSDevicePathSyntax,
!StartsWithDiskDesignatorAndBackslash(mResolvedPath));
MOZ_ASSERT_IF(
mUseDOSDevicePathSyntax,
!FilePreferences::StartsWithDiskDesignatorAndBackslash(mResolvedPath));
aResult = mResolvedPath;
return NS_OK;
@ -3075,7 +3065,7 @@ nsLocalFile::SetUseDOSDevicePathSyntax(bool aUseDOSDevicePathSyntax) {
mWorkingPath = Substring(mWorkingPath, kDevicePathSpecifier.Length());
}
} else {
if (StartsWithDiskDesignatorAndBackslash(mWorkingPath)) {
if (FilePreferences::StartsWithDiskDesignatorAndBackslash(mWorkingPath)) {
MakeDirty();
// Prepend the prefix
mWorkingPath = kDevicePathSpecifier + mWorkingPath;

Просмотреть файл

@ -140,3 +140,65 @@ TEST(FilePreferencesWin, AccessUNC)
rv = lf->InitWithPath(NS_LITERAL_STRING("\\\\nice\\..\\evil\\share"));
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
}
TEST(FilePreferencesWin, AccessDOSDevicePath)
{
const auto devicePathSpecifier = NS_LITERAL_STRING("\\\\?\\");
nsCOMPtr<nsIFile> lf = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
nsresult rv;
mozilla::FilePreferences::testing::SetBlockUNCPaths(true);
rv = lf->InitWithPath(devicePathSpecifier +
NS_LITERAL_STRING("evil\\z:\\share"));
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
rv = lf->InitWithPath(devicePathSpecifier +
NS_LITERAL_STRING("UNC\\evil\\share"));
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
rv = lf->InitWithPath(devicePathSpecifier + NS_LITERAL_STRING("C:\\"));
ASSERT_EQ(rv, NS_OK);
nsCOMPtr<nsIFile> base;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(base));
ASSERT_EQ(rv, NS_OK);
nsAutoString path;
rv = base->GetPath(path);
ASSERT_EQ(rv, NS_OK);
rv = lf->InitWithPath(devicePathSpecifier + path);
ASSERT_EQ(rv, NS_OK);
}
TEST(FilePreferencesWin, StartsWithDiskDesignatorAndBackslash)
{
bool result;
result = mozilla::FilePreferences::StartsWithDiskDesignatorAndBackslash(
NS_LITERAL_STRING("\\\\UNC\\path"));
ASSERT_FALSE(result);
result = mozilla::FilePreferences::StartsWithDiskDesignatorAndBackslash(
NS_LITERAL_STRING("\\single\\backslash"));
ASSERT_FALSE(result);
result = mozilla::FilePreferences::StartsWithDiskDesignatorAndBackslash(
NS_LITERAL_STRING("C:relative"));
ASSERT_FALSE(result);
result = mozilla::FilePreferences::StartsWithDiskDesignatorAndBackslash(
NS_LITERAL_STRING("\\\\?\\C:\\"));
ASSERT_FALSE(result);
result = mozilla::FilePreferences::StartsWithDiskDesignatorAndBackslash(
NS_LITERAL_STRING("C:\\"));
ASSERT_TRUE(result);
result = mozilla::FilePreferences::StartsWithDiskDesignatorAndBackslash(
NS_LITERAL_STRING("c:\\"));
ASSERT_TRUE(result);
}