Bug 1380690 - Part 2 - Whitelist repo and object dirs using paths from the Info.plist files. r=Alex_Gaynor,spohl
authorHaik Aftandilian <haftandilian@mozilla.com>
Wed, 19 Jul 2017 09:16:09 -0700
changeset 419379 225827d9f375cb452b8e2865150695217f355153
parent 419378 db6d7074529469c138e6f8ec361c54d4f203f45c
child 419380 33ecd88b5adbd17d97ce8e9cfcd004949f9963cd
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersAlex_Gaynor, spohl
bugs1380690
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1380690 - Part 2 - Whitelist repo and object dirs using paths from the Info.plist files. r=Alex_Gaynor,spohl On Mac developer builds, read the repo path and object dir path from the Info.plist files in the application bundle instead of the MOZ_DEVELOPER_REPO_DIR and MOZ_DEVELOPER_OBJ_DIR environment variables. MozReview-Commit-ID: 8UtjkNPGUM1
dom/ipc/ContentChild.cpp
netwerk/protocol/res/ExtensionProtocolHandler.cpp
security/sandbox/common/SandboxSettings.cpp
security/sandbox/common/SandboxSettings.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1453,33 +1453,16 @@ GetDirectoryPath(const char *aPath) {
   nsAutoCString directoryPath;
   if (NS_FAILED(directoryFile->GetNativePath(directoryPath))) {
     MOZ_CRASH("Failed to get path for an nsIFile");
   }
   return directoryPath;
 }
 #endif // DEBUG
 
-static nsresult
-NormalizePath(const char* aPath, nsCString& aOutPath)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsIFile> file;
-  rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(aPath), true, getter_AddRefs(file));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = file->Normalize();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = file->GetNativePath(aOutPath);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
-}
-
 static bool
 StartMacOSContentSandbox()
 {
   int sandboxLevel = GetEffectiveContentSandboxLevel();
   if (sandboxLevel < 1) {
     return false;
   }
 
@@ -1514,25 +1497,16 @@ StartMacOSContentSandbox()
     profileDir->Normalize();
     rv = profileDir->GetNativePath(profileDirPath);
     if (NS_FAILED(rv) || profileDirPath.IsEmpty()) {
       MOZ_CRASH("Failed to get profile path");
     }
   }
 
   bool isFileProcess = cc->GetRemoteType().EqualsLiteral(FILE_REMOTE_TYPE);
-  char *developer_repo_dir = nullptr;
-  char *developer_obj_dir = nullptr;
-  if (mozilla::IsDevelopmentBuild()) {
-    // If this is a developer build the resources in the .app are symlinks to
-    // outside of the .app. Therefore in non-release builds we allow reads from
-    // the whole repository. MOZ_DEVELOPER_REPO_DIR is set by mach run.
-    developer_repo_dir = PR_GetEnv("MOZ_DEVELOPER_REPO_DIR");
-    developer_obj_dir = PR_GetEnv("MOZ_DEVELOPER_OBJ_DIR");
-  }
 
   MacSandboxInfo info;
   info.type = MacSandboxType_Content;
   info.level = sandboxLevel;
   info.hasFilePrivileges = isFileProcess;
   info.shouldLog = Preferences::GetBool("security.sandbox.logging.enabled") ||
                    PR_GetEnv("MOZ_SANDBOX_LOGGING");
   info.appPath.assign(appPath.get());
@@ -1549,31 +1523,34 @@ StartMacOSContentSandbox()
     info.testingReadPath1.assign(testingReadPath1.get());
   }
   nsAdoptingCString testingReadPath2 =
     Preferences::GetCString("security.sandbox.content.mac.testing_read_path2");
   if (!testingReadPath2.IsEmpty()) {
     info.testingReadPath2.assign(testingReadPath2.get());
   }
 
-  if (developer_repo_dir) {
+  if (mozilla::IsDevelopmentBuild()) {
+    nsCOMPtr<nsIFile> repoDir;
+    rv = mozilla::GetRepoDir(getter_AddRefs(repoDir));
+    if (NS_FAILED(rv)) {
+      MOZ_CRASH("Failed to get path to repo dir");
+    }
     nsCString repoDirPath;
-    rv = NormalizePath(developer_repo_dir, repoDirPath);
-    if (NS_FAILED(rv)) {
-      MOZ_CRASH("Failed to normalize repo path");
-    }
+    Unused << repoDir->GetNativePath(repoDirPath);
     info.testingReadPath3.assign(repoDirPath.get());
-  }
-
-  if (developer_obj_dir) {
+
+    nsCOMPtr<nsIFile> objDir;
+    rv = mozilla::GetObjDir(getter_AddRefs(objDir));
+    if (NS_FAILED(rv)) {
+      MOZ_CRASH("Failed to get path to build object dir");
+    }
+
     nsCString objDirPath;
-    rv = NormalizePath(developer_obj_dir, objDirPath);
-    if (NS_FAILED(rv)) {
-      MOZ_CRASH("Failed to normalize obj dir path");
-    }
+    Unused << objDir->GetNativePath(objDirPath);
     info.testingReadPath4.assign(objDirPath.get());
   }
 
   if (profileDir) {
     info.hasSandboxedProfile = true;
     info.profileDir.assign(profileDirPath.get());
   } else {
     info.hasSandboxedProfile = false;
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -510,27 +510,21 @@ ExtensionProtocolHandler::SubstituteChan
 Result<Ok, nsresult>
 ExtensionProtocolHandler::DevRepoContains(nsIFile* aRequestedFile,
                                           bool *aResult)
 {
   MOZ_ASSERT(!IsNeckoChild());
   MOZ_ASSERT(aResult);
   *aResult = false;
 
-  // On the first invocation, set mDevRepo if this is a
-  // development build with MOZ_DEVELOPER_REPO_DIR set.
+  // On the first invocation, set mDevRepo if this is a development build
   if (!mAlreadyCheckedDevRepo) {
     mAlreadyCheckedDevRepo = true;
     if (mozilla::IsDevelopmentBuild()) {
-      char *developer_repo_dir = PR_GetEnv("MOZ_DEVELOPER_REPO_DIR");
-      if (developer_repo_dir) {
-        NS_TRY(NS_NewLocalFile(NS_ConvertUTF8toUTF16(developer_repo_dir),
-                               false, getter_AddRefs(mDevRepo)));
-        NS_TRY(mDevRepo->Normalize());
-      }
+      NS_TRY(mozilla::GetRepoDir(getter_AddRefs(mDevRepo)));
     }
   }
 
   if (mDevRepo) {
     // This is a development build
     NS_TRY(mDevRepo->Contains(aRequestedFile, aResult));
   }
 
--- a/security/sandbox/common/SandboxSettings.cpp
+++ b/security/sandbox/common/SandboxSettings.cpp
@@ -5,25 +5,158 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozISandboxSettings.h"
 
 #include "mozilla/Omnijar.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/Preferences.h"
 
+#include "nsDirectoryServiceDefs.h"
+
+#if defined(XP_MACOSX)
+#include <CoreServices/CoreServices.h>
+// Info.plist key associated with the developer repo path
+#define MAC_DEV_REPO_KEY "MozillaDeveloperRepoPath"
+// Info.plist key associated with the developer repo object directory
+#define MAC_DEV_OBJ_KEY "MozillaDeveloperObjPath"
+#else
+#include "prenv.h"
+#endif /* XP_MACOSX */
+
 namespace mozilla {
 
 bool IsDevelopmentBuild()
 {
   nsCOMPtr<nsIFile> path = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
   // If the path doesn't exist, we're a dev build.
   return path == nullptr;
 }
 
+#if defined(XP_MACOSX)
+/*
+ * Helper function to read a string value for a given key from the .app's
+ * Info.plist.
+ */
+static nsresult
+GetStringValueFromBundlePlist(const nsAString& aKey, nsAutoCString& aValue)
+{
+  CFBundleRef mainBundle = CFBundleGetMainBundle();
+
+  // Read this app's bundle Info.plist as a dictionary
+  CFDictionaryRef bundleInfoDict = CFBundleGetInfoDictionary(mainBundle);
+  if (bundleInfoDict == NULL) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoCString keyAutoCString = NS_ConvertUTF16toUTF8(aKey);
+  CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
+                                              keyAutoCString.get(),
+                                              kCFStringEncodingUTF8);
+
+  CFStringRef value = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, key);
+  const char* valueCString = CFStringGetCStringPtr(value,
+                                                   kCFStringEncodingUTF8);
+  aValue.Assign(valueCString);
+  CFRelease(key);
+
+  return NS_OK;
+}
+
+/*
+ * Helper function for reading a path string from the .app's Info.plist
+ * and returning a directory object for that path with symlinks resolved.
+ */
+static nsresult
+GetDirFromBundlePlist(const nsAString& aKey, nsIFile **aDir)
+{
+  nsresult rv;
+
+  nsAutoCString dirPath;
+  rv = GetStringValueFromBundlePlist(aKey, dirPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIFile> dir;
+  rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(dirPath),
+                       false,
+                       getter_AddRefs(dir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = dir->Normalize();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isDirectory = false;
+  rv = dir->IsDirectory(&isDirectory);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!isDirectory) {
+    return NS_ERROR_FILE_NOT_DIRECTORY;
+  }
+
+  dir.swap(*aDir);
+  return NS_OK;
+}
+
+#else /* !XP_MACOSX */
+
+/*
+ * Helper function for getting a directory object for a given env variable
+ */
+static nsresult
+GetDirFromEnv(const char* aEnvVar, nsIFile **aDir)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIFile> dir;
+  const char *dir_path = PR_GetEnv(aEnvVar);
+  if (!dir_path) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(dir_path),
+                       false,
+                       getter_AddRefs(dir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = dir->Normalize();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isDirectory = false;
+  rv = dir->IsDirectory(&isDirectory);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!isDirectory) {
+    return NS_ERROR_FILE_NOT_DIRECTORY;
+  }
+
+  dir.swap(*aDir);
+  return NS_OK;
+}
+#endif /* XP_MACOSX */
+
+nsresult
+GetRepoDir(nsIFile **aRepoDir)
+{
+  MOZ_ASSERT(IsDevelopmentBuild());
+#if defined(XP_MACOSX)
+  return GetDirFromBundlePlist(NS_LITERAL_STRING(MAC_DEV_REPO_KEY), aRepoDir);
+#else
+  return GetDirFromEnv("MOZ_DEVELOPER_REPO_DIR", aRepoDir);
+#endif /* XP_MACOSX */
+}
+
+nsresult
+GetObjDir(nsIFile **aObjDir)
+{
+  MOZ_ASSERT(IsDevelopmentBuild());
+#if defined(XP_MACOSX)
+  return GetDirFromBundlePlist(NS_LITERAL_STRING(MAC_DEV_OBJ_KEY), aObjDir);
+#else
+  return GetDirFromEnv("MOZ_DEVELOPER_OBJ_DIR", aObjDir);
+#endif /* XP_MACOSX */
+}
+
 int GetEffectiveContentSandboxLevel() {
   int level = Preferences::GetInt("security.sandbox.content.level");
 // On Windows and macOS, enforce a minimum content sandbox level of 1 (except on
 // Nightly, where it can be set to 0).
 #if !defined(NIGHTLY_BUILD) && (defined(XP_WIN) || defined(XP_MACOSX))
   if (level < 1) {
     level = 1;
   }
--- a/security/sandbox/common/SandboxSettings.h
+++ b/security/sandbox/common/SandboxSettings.h
@@ -14,10 +14,15 @@ namespace mozilla {
 int GetEffectiveContentSandboxLevel();
 
 // Returns whether or not the currently running build is a development build -
 // where development build means "the files in the .app are symlinks to the src
 // directory". This check is implemented by looking for omni.ja in
 // .app/Contents/Resources/.
 bool IsDevelopmentBuild();
 
+// Return the repo directory and the repo object directory respectively. These
+// should only be used on developer builds to determine the path to the repo
+// or object directory.
+nsresult GetRepoDir(nsIFile **aRepoDir);
+nsresult GetObjDir(nsIFile **aObjDir);
 }
 #endif // mozilla_SandboxPolicies_h