summaryrefslogtreecommitdiffstats
path: root/xpcom/io/FilePreferences.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/io/FilePreferences.cpp')
-rw-r--r--xpcom/io/FilePreferences.cpp272
1 files changed, 272 insertions, 0 deletions
diff --git a/xpcom/io/FilePreferences.cpp b/xpcom/io/FilePreferences.cpp
new file mode 100644
index 000000000..ef942beb2
--- /dev/null
+++ b/xpcom/io/FilePreferences.cpp
@@ -0,0 +1,272 @@
+/* -*- 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/Preferences.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;
+
+static Paths& PathArray()
+{
+ static Paths sPaths;
+ return sPaths;
+}
+
+static void AllowDirectory(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 (!PathArray().Contains(path)) {
+ PathArray().AppendElement(path);
+ }
+}
+
+void InitPrefs()
+{
+ sBlockUNCPaths = Preferences::GetBool("network.file.disable_unc_paths", false);
+}
+
+void InitDirectoriesWhitelist()
+{
+ // NS_GRE_DIR is the installation path where the binary resides.
+ AllowDirectory(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);
+}
+
+namespace { // anon
+
+class Normalizer
+{
+public:
+ Normalizer(const nsAString& aFilePath, const char16_t aSeparator);
+ bool Get(nsAString& aNormalizedFilePath);
+
+private:
+ bool ConsumeItem();
+ bool ConsumeSeparator();
+ bool IsEOF() { return mFilePathCursor == mFilePathEnd; }
+
+ bool ConsumeName();
+ bool CheckParentDir();
+ bool CheckCurrentDir();
+
+ nsString::const_char_iterator mFilePathCursor;
+ nsString::const_char_iterator mFilePathEnd;
+
+ nsDependentSubstring mItem;
+ char16_t const mSeparator;
+ nsTArray<nsDependentSubstring> mStack;
+};
+
+Normalizer::Normalizer(const nsAString& aFilePath, const char16_t aSeparator)
+ : mFilePathCursor(aFilePath.BeginReading())
+ , mFilePathEnd(aFilePath.EndReading())
+ , mSeparator(aSeparator)
+{
+}
+
+bool Normalizer::ConsumeItem()
+{
+ if (IsEOF()) {
+ return false;
+ }
+
+ nsString::const_char_iterator nameBegin = mFilePathCursor;
+ while (mFilePathCursor != mFilePathEnd) {
+ if (*mFilePathCursor == mSeparator) {
+ break; // don't include the separator
+ }
+ ++mFilePathCursor;
+ }
+
+ mItem.Rebind(nameBegin, mFilePathCursor);
+ return true;
+}
+
+bool Normalizer::ConsumeSeparator()
+{
+ if (IsEOF()) {
+ return false;
+ }
+
+ if (*mFilePathCursor != mSeparator) {
+ return false;
+ }
+
+ ++mFilePathCursor;
+ return true;
+}
+
+bool Normalizer::Get(nsAString& aNormalizedFilePath)
+{
+ aNormalizedFilePath.Truncate();
+
+ if (IsEOF()) {
+ return true;
+ }
+ if (ConsumeSeparator()) {
+ aNormalizedFilePath.Append(mSeparator);
+ }
+
+ if (IsEOF()) {
+ return true;
+ }
+ if (ConsumeSeparator()) {
+ aNormalizedFilePath.Append(mSeparator);
+ }
+
+ while (!IsEOF()) {
+ if (!ConsumeName()) {
+ return false;
+ }
+ }
+
+ for (auto const& name : mStack) {
+ aNormalizedFilePath.Append(name);
+ }
+
+ return true;
+}
+
+bool Normalizer::ConsumeName()
+{
+ if (!ConsumeItem()) {
+ return true;
+ }
+
+ if (CheckCurrentDir()) {
+ return true;
+ }
+
+ if (CheckParentDir()) {
+ if (!mStack.Length()) {
+ // This means there are more \.. than valid names
+ return false;
+ }
+
+ mStack.RemoveElementAt(mStack.Length() - 1);
+ return true;
+ }
+
+ if (mItem.IsEmpty()) {
+ // this means an empty name (a lone slash), which is illegal
+ return false;
+ }
+
+ if (ConsumeSeparator()) {
+ mItem.Rebind(mItem.BeginReading(), mFilePathCursor);
+ }
+ mStack.AppendElement(mItem);
+
+ return true;
+}
+
+bool Normalizer::CheckCurrentDir()
+{
+ if (mItem == NS_LITERAL_STRING(".")) {
+ ConsumeSeparator();
+ // EOF is acceptable
+ return true;
+ }
+
+ return false;
+}
+
+bool Normalizer::CheckParentDir()
+{
+ if (mItem == NS_LITERAL_STRING("..")) {
+ ConsumeSeparator();
+ // EOF is acceptable
+ return true;
+ }
+
+ return false;
+}
+
+} // anon
+
+bool IsBlockedUNCPath(const nsAString& aFilePath)
+{
+ if (!sBlockUNCPaths) {
+ return false;
+ }
+
+ if (!StringBeginsWith(aFilePath, NS_LITERAL_STRING("\\\\"))) {
+ return false;
+ }
+
+ nsAutoString normalized;
+ if (!Normalizer(aFilePath, L'\\').Get(normalized)) {
+ // Broken paths are considered invalid and thus inaccessible
+ return true;
+ }
+
+ for (const auto& allowedPrefix : PathArray()) {
+ 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;
+}
+
+void testing::SetBlockUNCPaths(bool aBlock)
+{
+ sBlockUNCPaths = aBlock;
+}
+
+void testing::AddDirectoryToWhitelist(nsAString const & aPath)
+{
+ PathArray().AppendElement(aPath);
+}
+
+bool testing::NormalizePath(nsAString const & aPath, nsAString & aNormalized)
+{
+ Normalizer normalizer(aPath, L'\\');
+ return normalizer.Get(aNormalized);
+}
+
+} // ::FilePreferences
+} // ::mozilla