зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1412081 - Add ability to blacklist file paths on Unix platforms r=mayhemer
--HG-- extra : rebase_source : 6894f5c3df745519e5e9db5b7bf6f004922152d1
This commit is contained in:
Родитель
29e57a2096
Коммит
5bd326c5f5
|
@ -6,25 +6,46 @@
|
|||
|
||||
#include "FilePreferences.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Tokenizer.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace FilePreferences {
|
||||
|
||||
static bool sBlockUNCPaths = false;
|
||||
typedef nsTArray<nsString> Paths;
|
||||
typedef nsTArray<nsString> WinPaths;
|
||||
|
||||
static Paths& PathArray()
|
||||
static WinPaths& PathWhitelist()
|
||||
{
|
||||
static Paths sPaths;
|
||||
static WinPaths sPaths;
|
||||
return sPaths;
|
||||
}
|
||||
|
||||
static void AllowDirectory(char const* directory)
|
||||
#ifdef XP_WIN
|
||||
typedef char16_t char_path_t;
|
||||
#else
|
||||
typedef char char_path_t;
|
||||
#endif
|
||||
|
||||
typedef nsTArray<nsTString<char_path_t>> Paths;
|
||||
static StaticAutoPtr<Paths> sBlacklist;
|
||||
|
||||
static Paths& PathBlacklist()
|
||||
{
|
||||
if (!sBlacklist) {
|
||||
sBlacklist = new nsTArray<nsTString<char_path_t>>();
|
||||
ClearOnShutdown(&sBlacklist);
|
||||
}
|
||||
return *sBlacklist;
|
||||
}
|
||||
|
||||
static void AllowUNCDirectory(char const* directory)
|
||||
{
|
||||
nsCOMPtr<nsIFile> file;
|
||||
NS_GetSpecialDirectory(directory, getter_AddRefs(file));
|
||||
|
@ -44,135 +65,160 @@ static void AllowDirectory(char const* directory)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!PathArray().Contains(path)) {
|
||||
PathArray().AppendElement(path);
|
||||
if (!PathWhitelist().Contains(path)) {
|
||||
PathWhitelist().AppendElement(path);
|
||||
}
|
||||
}
|
||||
|
||||
void InitPrefs()
|
||||
{
|
||||
sBlockUNCPaths = Preferences::GetBool("network.file.disable_unc_paths", false);
|
||||
|
||||
PathBlacklist().Clear();
|
||||
nsTAutoString<char_path_t> blacklist;
|
||||
#ifdef XP_WIN
|
||||
Preferences::GetString("network.file.path_blacklist", blacklist);
|
||||
#else
|
||||
Preferences::GetCString("network.file.path_blacklist", blacklist);
|
||||
#endif
|
||||
|
||||
TTokenizer<char_path_t> p(blacklist);
|
||||
while (!p.CheckEOF()) {
|
||||
nsTString<char_path_t> path;
|
||||
Unused << p.ReadUntil(TTokenizer<char_path_t>::Token::Char(','), path);
|
||||
path.Trim(" ");
|
||||
if (!path.IsEmpty()) {
|
||||
PathBlacklist().AppendElement(path);
|
||||
}
|
||||
Unused << p.CheckChar(',');
|
||||
}
|
||||
}
|
||||
|
||||
void InitDirectoriesWhitelist()
|
||||
{
|
||||
// NS_GRE_DIR is the installation path where the binary resides.
|
||||
AllowDirectory(NS_GRE_DIR);
|
||||
AllowUNCDirectory(NS_GRE_DIR);
|
||||
// NS_APP_USER_PROFILE_50_DIR and NS_APP_USER_PROFILE_LOCAL_50_DIR are the two
|
||||
// parts of the profile we store permanent and local-specific data.
|
||||
AllowDirectory(NS_APP_USER_PROFILE_50_DIR);
|
||||
AllowDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR);
|
||||
AllowUNCDirectory(NS_APP_USER_PROFILE_50_DIR);
|
||||
AllowUNCDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR);
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
class Normalizer : public Tokenizer16
|
||||
template <typename TChar>
|
||||
class TNormalizer
|
||||
: public TTokenizer<TChar>
|
||||
{
|
||||
typedef TTokenizer<TChar> base;
|
||||
public:
|
||||
Normalizer(const nsAString& aFilePath, const Token& aSeparator);
|
||||
bool Get(nsAString& aNormalizedFilePath);
|
||||
typedef typename base::Token Token;
|
||||
|
||||
TNormalizer(const nsTSubstring<TChar>& aFilePath, const Token& aSeparator)
|
||||
: TTokenizer<TChar>(aFilePath)
|
||||
, mSeparator(aSeparator)
|
||||
{
|
||||
}
|
||||
|
||||
bool Get(nsTSubstring<TChar>& aNormalizedFilePath)
|
||||
{
|
||||
aNormalizedFilePath.Truncate();
|
||||
|
||||
// Windows UNC paths begin with double separator (\\)
|
||||
// Linux paths begin with just one separator (/)
|
||||
// If we want to use the normalizer for regular windows paths this code
|
||||
// will need to be updated.
|
||||
#ifdef XP_WIN
|
||||
if (base::Check(mSeparator)) {
|
||||
aNormalizedFilePath.Append(mSeparator.AsChar());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (base::Check(mSeparator)) {
|
||||
aNormalizedFilePath.Append(mSeparator.AsChar());
|
||||
}
|
||||
|
||||
while (base::HasInput()) {
|
||||
if (!ConsumeName()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& name : mStack) {
|
||||
aNormalizedFilePath.Append(name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
bool ConsumeName();
|
||||
bool CheckParentDir();
|
||||
bool CheckCurrentDir();
|
||||
bool CheckSeparator();
|
||||
|
||||
Token const mSeparator;
|
||||
nsTArray<nsDependentSubstring> mStack;
|
||||
};
|
||||
|
||||
Normalizer::Normalizer(const nsAString& aFilePath, const Token& aSeparator)
|
||||
: Tokenizer16(aFilePath)
|
||||
, mSeparator(aSeparator)
|
||||
{
|
||||
}
|
||||
|
||||
bool Normalizer::Get(nsAString& aNormalizedFilePath)
|
||||
{
|
||||
aNormalizedFilePath.Truncate();
|
||||
|
||||
if (Check(mSeparator)) {
|
||||
aNormalizedFilePath.Append(mSeparator.AsChar());
|
||||
}
|
||||
if (Check(mSeparator)) {
|
||||
aNormalizedFilePath.Append(mSeparator.AsChar());
|
||||
}
|
||||
|
||||
while (HasInput()) {
|
||||
if (!ConsumeName()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& name : mStack) {
|
||||
aNormalizedFilePath.Append(name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Normalizer::ConsumeName()
|
||||
{
|
||||
if (CheckEOF()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckCurrentDir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckParentDir()) {
|
||||
if (!mStack.Length()) {
|
||||
// This means there are more \.. than valid names
|
||||
return false;
|
||||
bool ConsumeName()
|
||||
{
|
||||
if (base::CheckEOF()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mStack.RemoveLastElement();
|
||||
if (CheckCurrentDir()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckParentDir()) {
|
||||
if (!mStack.Length()) {
|
||||
// This means there are more \.. than valid names
|
||||
return false;
|
||||
}
|
||||
|
||||
mStack.RemoveLastElement();
|
||||
return true;
|
||||
}
|
||||
|
||||
nsTDependentSubstring<TChar> name;
|
||||
if (base::ReadUntil(mSeparator, name, base::INCLUDE_LAST) && name.Length() == 1) {
|
||||
// this means and empty name (a lone slash), which is illegal
|
||||
return false;
|
||||
}
|
||||
mStack.AppendElement(name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsDependentSubstring name;
|
||||
if (ReadUntil(mSeparator, name, INCLUDE_LAST) && name.Length() == 1) {
|
||||
// this means and empty name (a lone slash), which is illegal
|
||||
bool CheckParentDir()
|
||||
{
|
||||
typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
|
||||
if (base::CheckChar('.') && base::CheckChar('.') && CheckSeparator()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
base::mCursor = cursor;
|
||||
return false;
|
||||
}
|
||||
mStack.AppendElement(name);
|
||||
|
||||
return true;
|
||||
}
|
||||
bool CheckCurrentDir()
|
||||
{
|
||||
typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
|
||||
if (base::CheckChar('.') && CheckSeparator()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Normalizer::CheckCurrentDir()
|
||||
{
|
||||
nsString::const_char_iterator cursor = mCursor;
|
||||
if (CheckChar('.') && CheckSeparator()) {
|
||||
return true;
|
||||
base::mCursor = cursor;
|
||||
return false;
|
||||
}
|
||||
|
||||
mCursor = cursor;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Normalizer::CheckParentDir()
|
||||
{
|
||||
nsString::const_char_iterator cursor = mCursor;
|
||||
if (CheckChar('.') && CheckChar('.') && CheckSeparator()) {
|
||||
return true;
|
||||
bool CheckSeparator()
|
||||
{
|
||||
return base::Check(mSeparator) || base::CheckEOF();
|
||||
}
|
||||
|
||||
mCursor = cursor;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Normalizer::CheckSeparator()
|
||||
{
|
||||
return Check(mSeparator) || CheckEOF();
|
||||
}
|
||||
Token const mSeparator;
|
||||
nsTArray<nsTDependentSubstring<TChar>> mStack;
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
bool IsBlockedUNCPath(const nsAString& aFilePath)
|
||||
{
|
||||
typedef TNormalizer<char16_t> Normalizer;
|
||||
if (!sBlockUNCPaths) {
|
||||
return false;
|
||||
}
|
||||
|
@ -187,7 +233,7 @@ bool IsBlockedUNCPath(const nsAString& aFilePath)
|
|||
return true;
|
||||
}
|
||||
|
||||
for (const auto& allowedPrefix : PathArray()) {
|
||||
for (const auto& allowedPrefix : PathWhitelist()) {
|
||||
if (StringBeginsWith(normalized, allowedPrefix)) {
|
||||
if (normalized.Length() == allowedPrefix.Length()) {
|
||||
return false;
|
||||
|
@ -207,6 +253,44 @@ bool IsBlockedUNCPath(const nsAString& aFilePath)
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
const char kPathSeparator = '\\';
|
||||
#else
|
||||
const char kPathSeparator = '/';
|
||||
#endif
|
||||
|
||||
bool IsAllowedPath(const nsTSubstring<char_path_t>& aFilePath)
|
||||
{
|
||||
typedef TNormalizer<char_path_t> Normalizer;
|
||||
// If sBlacklist has been cleared at shutdown, we must avoid calling
|
||||
// PathBlacklist() again, as that will recreate the array and we will leak.
|
||||
if (!sBlacklist) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PathBlacklist().Length() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsTAutoString<char_path_t> normalized;
|
||||
if (!Normalizer(aFilePath, Normalizer::Token::Char(kPathSeparator)).Get(normalized)) {
|
||||
// Broken paths are considered invalid and thus inaccessible
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& prefix : PathBlacklist()) {
|
||||
if (StringBeginsWith(normalized, prefix)) {
|
||||
if (normalized.Length() > prefix.Length() &&
|
||||
normalized[prefix.Length()] != kPathSeparator) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void testing::SetBlockUNCPaths(bool aBlock)
|
||||
{
|
||||
sBlockUNCPaths = aBlock;
|
||||
|
@ -214,11 +298,12 @@ void testing::SetBlockUNCPaths(bool aBlock)
|
|||
|
||||
void testing::AddDirectoryToWhitelist(nsAString const & aPath)
|
||||
{
|
||||
PathArray().AppendElement(aPath);
|
||||
PathWhitelist().AppendElement(aPath);
|
||||
}
|
||||
|
||||
bool testing::NormalizePath(nsAString const & aPath, nsAString & aNormalized)
|
||||
{
|
||||
typedef TNormalizer<char16_t> Normalizer;
|
||||
Normalizer normalizer(aPath, Normalizer::Token::Char('\\'));
|
||||
return normalizer.Get(aNormalized);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,12 @@ void InitPrefs();
|
|||
void InitDirectoriesWhitelist();
|
||||
bool IsBlockedUNCPath(const nsAString& aFilePath);
|
||||
|
||||
#ifdef XP_WIN
|
||||
bool IsAllowedPath(const nsAString& aFilePath);
|
||||
#else
|
||||
bool IsAllowedPath(const nsACString& aFilePath);
|
||||
#endif
|
||||
|
||||
namespace testing {
|
||||
|
||||
void SetBlockUNCPaths(bool aBlock);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/FilePreferences.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -84,6 +85,8 @@ using namespace mozilla;
|
|||
do { \
|
||||
if (mPath.IsEmpty()) \
|
||||
return NS_ERROR_NOT_INITIALIZED; \
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) \
|
||||
return NS_ERROR_FILE_ACCESS_DENIED; \
|
||||
} while(0)
|
||||
|
||||
/* directory enumerator */
|
||||
|
@ -139,6 +142,13 @@ nsDirEnumeratorUnix::Init(nsLocalFile* aParent,
|
|||
return NS_ERROR_FILE_INVALID_PATH;
|
||||
}
|
||||
|
||||
// When enumerating the directory, the paths must have a slash at the end.
|
||||
nsAutoCString dirPathWithSlash(dirPath);
|
||||
dirPathWithSlash.Append('/');
|
||||
if (!FilePreferences::IsAllowedPath(dirPathWithSlash)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (NS_FAILED(aParent->GetNativePath(mParentPath))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -270,6 +280,11 @@ nsLocalFile::nsLocalFileConstructor(nsISupports* aOuter,
|
|||
bool
|
||||
nsLocalFile::FillStatCache()
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
errno = EACCES;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (STAT(mPath.get(), &mCachedStat) == -1) {
|
||||
// try lstat it may be a symlink
|
||||
if (LSTAT(mPath.get(), &mCachedStat) == -1) {
|
||||
|
@ -312,6 +327,11 @@ nsLocalFile::InitWithNativePath(const nsACString& aFilePath)
|
|||
mPath = aFilePath;
|
||||
}
|
||||
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
mPath.Truncate();
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// trim off trailing slashes
|
||||
ssize_t len = mPath.Length();
|
||||
while ((len > 1) && (mPath[len - 1] == '/')) {
|
||||
|
@ -325,6 +345,10 @@ nsLocalFile::InitWithNativePath(const nsACString& aFilePath)
|
|||
NS_IMETHODIMP
|
||||
nsLocalFile::CreateAllAncestors(uint32_t aPermissions)
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// <jband> I promise to play nice
|
||||
char* buffer = mPath.BeginWriting();
|
||||
char* slashp = buffer;
|
||||
|
@ -396,6 +420,9 @@ NS_IMETHODIMP
|
|||
nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
|
||||
PRFileDesc** aResult)
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
*aResult = PR_Open(mPath.get(), aFlags, aMode);
|
||||
if (!*aResult) {
|
||||
return NS_ErrorAccordingToNSPR();
|
||||
|
@ -417,6 +444,9 @@ nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
|
|||
NS_IMETHODIMP
|
||||
nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult)
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
*aResult = fopen(mPath.get(), aMode);
|
||||
if (!*aResult) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -443,6 +473,10 @@ nsresult
|
|||
nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags,
|
||||
uint32_t aPermissions, PRFileDesc** aResult)
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (aType != NORMAL_FILE_TYPE && aType != DIRECTORY_TYPE) {
|
||||
return NS_ERROR_FILE_UNKNOWN_TYPE;
|
||||
}
|
||||
|
@ -492,6 +526,10 @@ nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags,
|
|||
NS_IMETHODIMP
|
||||
nsLocalFile::Create(uint32_t aType, uint32_t aPermissions)
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
PRFileDesc* junk = nullptr;
|
||||
nsresult rv = CreateAndKeepOpen(aType,
|
||||
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
|
||||
|
@ -547,6 +585,10 @@ nsLocalFile::Normalize()
|
|||
char resolved_path[PATH_MAX] = "";
|
||||
char* resolved_path_ptr = nullptr;
|
||||
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
resolved_path_ptr = realpath(mPath.get(), resolved_path);
|
||||
|
||||
// if there is an error, the return is null.
|
||||
|
@ -1011,6 +1053,10 @@ nsLocalFile::MoveToNative(nsIFile* aNewParent, const nsACString& aNewName)
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (!FilePreferences::IsAllowedPath(newPathName)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// try for atomic rename, falling back to copy/delete
|
||||
if (rename(mPath.get(), newPathName.get()) < 0) {
|
||||
if (errno == EXDEV) {
|
||||
|
@ -1953,6 +1999,10 @@ nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
|
|||
NS_IMETHODIMP
|
||||
nsLocalFile::Reveal()
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
|
||||
if (!giovfs) {
|
||||
|
@ -1996,6 +2046,10 @@ nsLocalFile::Reveal()
|
|||
NS_IMETHODIMP
|
||||
nsLocalFile::Launch()
|
||||
{
|
||||
if (!FilePreferences::IsAllowedPath(mPath)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
|
||||
if (!giovfs) {
|
||||
|
@ -2150,6 +2204,10 @@ nsLocalFile::RenameToNative(nsIFile* aNewParentDir, const nsACString& aNewName)
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (!FilePreferences::IsAllowedPath(newPathName)) {
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// try for atomic rename
|
||||
if (rename(mPath.get(), newPathName.get()) < 0) {
|
||||
if (errno == EXDEV) {
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
#include "gtest/gtest.h"
|
||||
|
||||
#include "mozilla/FilePreferences.h"
|
||||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
TEST(TestFilePreferencesUnix, Parsing)
|
||||
{
|
||||
#define kBlacklisted "/tmp/blacklisted"
|
||||
#define kBlacklistedDir "/tmp/blacklisted/"
|
||||
#define kBlacklistedFile "/tmp/blacklisted/file"
|
||||
#define kOther "/tmp/other"
|
||||
#define kOtherDir "/tmp/other/"
|
||||
#define kOtherFile "/tmp/other/file"
|
||||
#define kAllowed "/tmp/allowed"
|
||||
|
||||
// This is run on exit of this function to make sure we clear the pref
|
||||
// and that behaviour with the pref cleared is correct.
|
||||
auto cleanup = MakeScopeExit([&] {
|
||||
nsresult rv = Preferences::ClearUser("network.file.path_blacklist");
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
FilePreferences::InitPrefs();
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklisted)), true);
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedDir)), true);
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedFile)), true);
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kAllowed)), true);
|
||||
});
|
||||
|
||||
auto CheckPrefs = [](const nsACString& aPaths)
|
||||
{
|
||||
nsresult rv;
|
||||
rv = Preferences::SetCString("network.file.path_blacklist", aPaths);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
FilePreferences::InitPrefs();
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedDir)), false);
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedDir)), false);
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklistedFile)), false);
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kBlacklisted)), false);
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kAllowed)), true);
|
||||
};
|
||||
|
||||
CheckPrefs(NS_LITERAL_CSTRING(kBlacklisted));
|
||||
CheckPrefs(NS_LITERAL_CSTRING(kBlacklisted "," kOther));
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kOtherFile)), false);
|
||||
CheckPrefs(NS_LITERAL_CSTRING(kBlacklisted "," kOther ","));
|
||||
ASSERT_EQ(FilePreferences::IsAllowedPath(NS_LITERAL_CSTRING(kOtherFile)), false);
|
||||
}
|
||||
|
||||
TEST(TestFilePreferencesUnix, Simple)
|
||||
{
|
||||
nsAutoCString tempPath;
|
||||
|
||||
// This is the directory we will blacklist
|
||||
nsCOMPtr<nsIFile> blacklistedDir;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(blacklistedDir));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = blacklistedDir->GetNativePath(tempPath);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = blacklistedDir->AppendNative(NS_LITERAL_CSTRING("blacklisted_dir"));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
|
||||
// This is executed at exit to clean up after ourselves.
|
||||
auto cleanup = MakeScopeExit([&] {
|
||||
nsresult rv = Preferences::ClearUser("network.file.path_blacklist");
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
FilePreferences::InitPrefs();
|
||||
|
||||
rv = blacklistedDir->Remove(true);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
});
|
||||
|
||||
// Create the directory
|
||||
rv = blacklistedDir->Create(nsIFile::DIRECTORY_TYPE, 0666);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
|
||||
// This is the file we will try to access
|
||||
nsCOMPtr<nsIFile> blacklistedFile;
|
||||
rv = blacklistedDir->Clone(getter_AddRefs(blacklistedFile));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = blacklistedFile->AppendNative(NS_LITERAL_CSTRING("test_file"));
|
||||
|
||||
// Create the file
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = blacklistedFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
|
||||
|
||||
// Get the path for the blacklist
|
||||
nsAutoCString blackListPath;
|
||||
rv = blacklistedDir->GetNativePath(blackListPath);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
|
||||
// Set the pref and make sure it is enforced
|
||||
rv = Preferences::SetCString("network.file.path_blacklist", blackListPath);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
FilePreferences::InitPrefs();
|
||||
|
||||
// Check that we can't access some of the file attributes
|
||||
int64_t size;
|
||||
rv = blacklistedFile->GetFileSize(&size);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
bool exists;
|
||||
rv = blacklistedFile->Exists(&exists);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
// Check that we can't enumerate the directory
|
||||
nsCOMPtr<nsISimpleEnumerator> dirEnumerator;
|
||||
rv = blacklistedDir->GetDirectoryEntries(getter_AddRefs(dirEnumerator));
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
nsCOMPtr<nsIFile> newPath;
|
||||
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->AppendNative(NS_LITERAL_CSTRING("."));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->AppendNative(NS_LITERAL_CSTRING("blacklisted_dir"));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->Exists(&exists);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
rv = newPath->AppendNative(NS_LITERAL_CSTRING("test_file"));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->Exists(&exists);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
// Check that ./ does not bypass the filter
|
||||
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->AppendRelativeNativePath(NS_LITERAL_CSTRING("./blacklisted_dir/file"));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->Exists(&exists);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
// Check that .. does not bypass the filter
|
||||
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->AppendRelativeNativePath(NS_LITERAL_CSTRING("allowed/../blacklisted_dir/file"));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->Exists(&exists);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(newPath));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->AppendNative(NS_LITERAL_CSTRING("allowed"));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->AppendNative(NS_LITERAL_CSTRING(".."));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->AppendNative(NS_LITERAL_CSTRING("blacklisted_dir"));
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
rv = newPath->Exists(&exists);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
nsAutoCString trickyPath(tempPath);
|
||||
trickyPath.AppendLiteral("/allowed/../blacklisted_dir/file");
|
||||
rv = newPath->InitWithNativePath(trickyPath);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
// Check that we can't construct a path that is functionally the same
|
||||
// as the blacklisted one and bypasses the filter.
|
||||
trickyPath = tempPath;
|
||||
trickyPath.AppendLiteral("/./blacklisted_dir/file");
|
||||
rv = newPath->InitWithNativePath(trickyPath);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
trickyPath = tempPath;
|
||||
trickyPath.AppendLiteral("//blacklisted_dir/file");
|
||||
rv = newPath->InitWithNativePath(trickyPath);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
trickyPath.Truncate();
|
||||
trickyPath.AppendLiteral("//");
|
||||
trickyPath.Append(tempPath);
|
||||
trickyPath.AppendLiteral("/blacklisted_dir/file");
|
||||
rv = newPath->InitWithNativePath(trickyPath);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
trickyPath.Truncate();
|
||||
trickyPath.AppendLiteral("//");
|
||||
trickyPath.Append(tempPath);
|
||||
trickyPath.AppendLiteral("//blacklisted_dir/file");
|
||||
rv = newPath->InitWithNativePath(trickyPath);
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
|
||||
// Check that if the blacklisted string is a directory, we only block access
|
||||
// to subresources, not the directory itself.
|
||||
nsAutoCString blacklistDirPath(blackListPath);
|
||||
blacklistDirPath.Append("/");
|
||||
rv = Preferences::SetCString("network.file.path_blacklist", blacklistDirPath);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
FilePreferences::InitPrefs();
|
||||
|
||||
// This should work, since we only block subresources
|
||||
rv = blacklistedDir->Exists(&exists);
|
||||
ASSERT_EQ(rv, NS_OK);
|
||||
|
||||
rv = blacklistedDir->GetDirectoryEntries(getter_AddRefs(dirEnumerator));
|
||||
ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
|
||||
}
|
|
@ -76,6 +76,11 @@ if CONFIG['OS_TARGET'] == 'WINNT':
|
|||
UNIFIED_SOURCES += [
|
||||
'TestFilePreferencesWin.cpp',
|
||||
]
|
||||
else:
|
||||
UNIFIED_SOURCES += [
|
||||
'TestFilePreferencesUnix.cpp',
|
||||
]
|
||||
|
||||
|
||||
if CONFIG['WRAP_STL_INCLUDES'] and CONFIG['CC_TYPE'] != 'clang-cl':
|
||||
UNIFIED_SOURCES += [
|
||||
|
|
Загрузка…
Ссылка в новой задаче