Bug 1361733 - In debug builds, do not allow content sandbox to write to all of /private/var draft
authorAlex Gaynor <agaynor@mozilla.com>
Tue, 02 May 2017 11:07:10 -0400
changeset 574243 444e372b138f9b18d3e678f377c97542c02e4a8f
parent 574065 1fda52a1f3b81cf1a821155998dca637bb64e3d9
child 627515 ed1bb8ac8169af3df57fbfddf04cd0777a869944
push id57621
push userbmo:agaynor@mozilla.com
push dateMon, 08 May 2017 14:16:01 +0000
bugs1361733
milestone55.0a1
Bug 1361733 - In debug builds, do not allow content sandbox to write to all of /private/var This permission was needed for the memory bloat logging, which is used for leaktest, including logging intentionally crashing processes. Now we restrict ourselves to only allowing writes to the location needed for this logging, rather than all of /private/var. MozReview-Commit-ID: 5AbJEZlDHNV
dom/ipc/ContentChild.cpp
security/sandbox/mac/Sandbox.h
security/sandbox/mac/Sandbox.mm
security/sandbox/mac/SandboxPolicies.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1274,16 +1274,40 @@ GetAppPaths(nsCString &aAppPath, nsCStri
 static 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;
 }
 
+// This function is only used in an |#ifdef DEBUG| path.
+#ifdef DEBUG
+// Given a path to a file, return the directory which contains it.
+static nsAutoCString
+GetDirectoryPath(const char *aPath) {
+  nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  if (!file ||
+      NS_FAILED(file->InitWithNativePath(nsDependentCString(aPath)))) {
+    MOZ_CRASH("Failed to create or init an nsIFile");
+  }
+  nsCOMPtr<nsIFile> directoryFile;
+  if (NS_FAILED(file->GetParent(getter_AddRefs(directoryFile))) ||
+      !directoryFile) {
+    MOZ_CRASH("Failed to get parent for an nsIFile");
+  }
+  directoryFile->Normalize();
+  nsAutoCString directoryPath;
+  if (NS_FAILED(directoryFile->GetNativePath(directoryPath))) {
+    MOZ_CRASH("Failed to get path for an nsIFile");
+  }
+  return directoryPath;
+}
+#endif // DEBUG
+
 static bool
 StartMacOSContentSandbox()
 {
   int sandboxLevel = Preferences::GetInt("security.sandbox.content.level");
   if (sandboxLevel < 1) {
     return false;
   }
 
@@ -1347,16 +1371,29 @@ StartMacOSContentSandbox()
 
   if (profileDir) {
     info.hasSandboxedProfile = true;
     info.profileDir.assign(profileDirPath.get());
   } else {
     info.hasSandboxedProfile = false;
   }
 
+#ifdef DEBUG
+  // When a content process dies intentionally (|NoteIntentionalCrash|), for
+  // tests it wants to log that it did this. Allow writing to this location
+  // that the testrunner wants.
+  char *bloatLog = PR_GetEnv("XPCOM_MEM_BLOAT_LOG");
+  if (bloatLog != nullptr) {
+    // |bloatLog| points to a specific file, but we actually write to a sibling
+    // of that path.
+    nsAutoCString bloatDirectoryPath = GetDirectoryPath(bloatLog);
+    info.debugWriteDir.assign(bloatDirectoryPath.get());
+  }
+#endif // DEBUG
+
   std::string err;
   if (!mozilla::StartMacSandbox(info, err)) {
     NS_WARNING(err.c_str());
     MOZ_CRASH("sandbox_init() failed");
   }
 
   return true;
 }
--- a/security/sandbox/mac/Sandbox.h
+++ b/security/sandbox/mac/Sandbox.h
@@ -43,27 +43,29 @@ typedef struct _MacSandboxInfo {
       shouldLog(true) {}
   _MacSandboxInfo(const struct _MacSandboxInfo& other)
     : type(other.type), level(other.level),
       hasFilePrivileges(other.hasFilePrivileges),
       hasSandboxedProfile(other.hasSandboxedProfile),
       pluginInfo(other.pluginInfo),
       appPath(other.appPath), appBinaryPath(other.appBinaryPath),
       appDir(other.appDir), appTempDir(other.appTempDir),
-      profileDir(other.profileDir), shouldLog(other.shouldLog) {}
+      profileDir(other.profileDir), debugWriteDir(other.debugWriteDir),
+      shouldLog(other.shouldLog) {}
   MacSandboxType type;
   int32_t level;
   bool hasFilePrivileges;
   bool hasSandboxedProfile;
   MacSandboxPluginInfo pluginInfo;
   std::string appPath;
   std::string appBinaryPath;
   std::string appDir;
   std::string appTempDir;
   std::string profileDir;
+  std::string debugWriteDir;
   bool shouldLog;
 } MacSandboxInfo;
 
 namespace mozilla {
 
 bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage);
 
 } // namespace mozilla
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -173,22 +173,22 @@ bool StartMacSandbox(MacSandboxInfo aInf
       params.push_back("PROFILE_DIR");
       params.push_back(aInfo.profileDir.c_str());
       params.push_back("HOME_PATH");
       params.push_back(getenv("HOME"));
       params.push_back("HAS_SANDBOXED_PROFILE");
       params.push_back(aInfo.hasSandboxedProfile ? "TRUE" : "FALSE");
       params.push_back("HAS_FILE_PRIVILEGES");
       params.push_back(aInfo.hasFilePrivileges ? "TRUE" : "FALSE");
-      params.push_back("DEBUG_BUILD");
 #ifdef DEBUG
-      params.push_back("TRUE");
-#else
-      params.push_back("FALSE");
-#endif
+      if (!aInfo.debugWriteDir.empty()) {
+        params.push_back("DEBUG_WRITE_DIR");
+        params.push_back(aInfo.debugWriteDir.c_str());
+      }
+#endif // DEBUG
     } else {
       fprintf(stderr,
         "Content sandbox disabled due to sandbox level setting\n");
       return false;
     }
   }
   else {
     char *msg = NULL;
--- a/security/sandbox/mac/SandboxPolicies.h
+++ b/security/sandbox/mac/SandboxPolicies.h
@@ -44,30 +44,30 @@ static const char pluginSandboxRules[] =
 
 static const char widevinePluginSandboxRulesAddend[] = R"(
   (allow mach-lookup (global-name "com.apple.windowserver.active"))
 )";
 
 static const char contentSandboxRules[] = R"(
   (version 1)
 
-      (define should-log (param "SHOULD_LOG"))
+  (define should-log (param "SHOULD_LOG"))
   (define sandbox-level-1 (param "SANDBOX_LEVEL_1"))
   (define sandbox-level-2 (param "SANDBOX_LEVEL_2"))
   (define sandbox-level-3 (param "SANDBOX_LEVEL_3"))
   (define macosMinorVersion-9 (param "MAC_OS_MINOR_9"))
   (define appPath (param "APP_PATH"))
   (define appBinaryPath (param "APP_BINARY_PATH"))
   (define appDir (param "APP_DIR"))
   (define appTempDir (param "APP_TEMP_DIR"))
   (define hasProfileDir (param "HAS_SANDBOXED_PROFILE"))
   (define profileDir (param "PROFILE_DIR"))
   (define home-path (param "HOME_PATH"))
   (define hasFilePrivileges (param "HAS_FILE_PRIVILEGES"))
-  (define isDebugBuild (param "DEBUG_BUILD"))
+  (define debugWriteDir (param "DEBUG_WRITE_DIR"))
 
   ; Allow read access to standard system paths.
   (allow file-read*
     (require-all (file-mode #o0004)
       (require-any (subpath "/Library/Filesystems/NetFSPlugins")
         (subpath "/System")
         (subpath "/usr/lib")
         (subpath "/usr/share"))))
@@ -219,18 +219,18 @@ static const char contentSandboxRules[] 
 
     (allow file-read-metadata (home-subpath "/Library"))
 
     (allow file-read-metadata
       (literal "/private/var")
       (subpath "/private/var/folders"))
 
   ; bug 1303987
-    (if (string=? isDebugBuild "TRUE")
-        (allow file-write* (subpath "/private/var")))
+    (if (string? debugWriteDir)
+      (allow file-write* (subpath debugWriteDir)))
 
   ; bug 1324610
     (allow network-outbound (literal "/private/var/run/cupsd"))
 
     (allow-shared-list "org.mozilla.plugincontainer")
 
   ; the following rule should be removed when microphone access
   ; is brokered through the content process
@@ -317,18 +317,17 @@ static const char contentSandboxRules[] 
         (iokit-user-client-class "AppleGraphicsPolicyClient"))
 
   ; bug 1153809
     (allow iokit-open
         (iokit-user-client-class "NVDVDContextTesla")
         (iokit-user-client-class "Gen6DVDContext"))
 
   ; bug 1237847
-    (allow file-read*
+    (allow file-read* file-write*
         (subpath appTempDir))
-    (allow file-write*
-        (subpath appTempDir))
+
   )
 )";
 
 }
 
 #endif // mozilla_SandboxPolicies_h