gecko-dev/xpcom/io/FilePreferences.cpp
Sylvestre Ledru 265e672179 Bug 1511181 - Reformat everything to the Google coding style r=ehsan a=clang-format
# ignore-this-changeset

--HG--
extra : amend_source : 4d301d3b0b8711c4692392aa76088ba7fd7d1022
2018-11-30 11:46:48 +01:00

292 lines
7.4 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#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> WinPaths;
static WinPaths& PathWhitelist() {
static WinPaths sPaths;
return sPaths;
}
#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));
if (!file) {
return;
}
nsString path;
if (NS_FAILED(file->GetTarget(path))) {
return;
}
// The whitelist makes sense only for UNC paths, because this code is used
// to block only UNC paths, hence, no need to add non-UNC directories here
// as those would never pass the check.
if (!StringBeginsWith(path, NS_LITERAL_STRING("\\\\"))) {
return;
}
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.
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.
AllowUNCDirectory(NS_APP_USER_PROFILE_50_DIR);
AllowUNCDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR);
}
namespace { // anon
template <typename TChar>
class TNormalizer : public TTokenizer<TChar> {
typedef TTokenizer<TChar> base;
public:
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() {
if (base::CheckEOF()) {
return true;
}
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;
}
bool CheckParentDir() {
typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
if (base::CheckChar('.') && base::CheckChar('.') && CheckSeparator()) {
return true;
}
base::mCursor = cursor;
return false;
}
bool CheckCurrentDir() {
typename nsTString<TChar>::const_char_iterator cursor = base::mCursor;
if (base::CheckChar('.') && CheckSeparator()) {
return true;
}
base::mCursor = cursor;
return false;
}
bool CheckSeparator() { return base::Check(mSeparator) || base::CheckEOF(); }
Token const mSeparator;
nsTArray<nsTDependentSubstring<TChar>> mStack;
};
} // namespace
bool IsBlockedUNCPath(const nsAString& aFilePath) {
typedef TNormalizer<char16_t> Normalizer;
if (!sBlockUNCPaths) {
return false;
}
if (!StringBeginsWith(aFilePath, NS_LITERAL_STRING("\\\\"))) {
return false;
}
nsAutoString normalized;
if (!Normalizer(aFilePath, Normalizer::Token::Char('\\')).Get(normalized)) {
// Broken paths are considered invalid and thus inaccessible
return true;
}
for (const auto& allowedPrefix : PathWhitelist()) {
if (StringBeginsWith(normalized, allowedPrefix)) {
if (normalized.Length() == allowedPrefix.Length()) {
return false;
}
if (normalized[allowedPrefix.Length()] == L'\\') {
return false;
}
// When we are here, the path has a form "\\path\prefixevil"
// while we have an allowed prefix of "\\path\prefix".
// Note that we don't want to add a slash to the end of a prefix
// so that opening the directory (no slash at the end) still works.
break;
}
}
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; }
void testing::AddDirectoryToWhitelist(nsAString const& 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);
}
} // namespace FilePreferences
} // namespace mozilla