Bug 1380690 - Part 2 - Whitelist repo and object dirs using paths from the Info.plist files. r=Alex_Gaynor, r=spohl draft
authorHaik Aftandilian <haftandilian@mozilla.com>
Wed, 19 Jul 2017 09:16:09 -0700
changeset 611364 38d9b36592d492c40c6c5d2b91a920dad3901bf8
parent 610329 517a0e357c5e2c633335bb0ec727f7a36c8df5b0
child 614570 3f04c948b0b9eccf065d7c79d1f338e87998a647
push id69199
push userhaftandilian@mozilla.com
push dateWed, 19 Jul 2017 16:36:22 +0000
reviewersAlex_Gaynor, spohl
bugs1380690
milestone56.0a1
Bug 1380690 - Part 2 - Whitelist repo and object dirs using paths from the Info.plist files. r=Alex_Gaynor, r=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
@@ -1458,33 +1458,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;
   }
 
@@ -1519,25 +1502,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());
@@ -1554,31 +1528,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