Bug 1171796: Add sandbox rule for child process NSPR log file on Windows. r=bbondy
authorBob Owen <bobowencode@gmail.com>
Thu, 30 Jul 2015 10:04:42 +0100
changeset 287101 890f4c8154fa21c4ea05cfd6002f67ce7d9d9d7e
parent 287100 bce7955eaa1363cbc141596b0fa74276aa33b9fe
child 287102 48a35f84fb9dbc895ca66fcd1bbde5f85c9f90b9
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbondy
bugs1171796
milestone42.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 1171796: Add sandbox rule for child process NSPR log file on Windows. r=bbondy This also moves the initialization of the sandbox TargetServices to earlier in plugin-container.cpp content_process_main, because it needs to happen before xul.dll loads.
ipc/contentproc/plugin-container.cpp
ipc/glue/GeckoChildProcessHost.cpp
security/sandbox/win/src/sandboxtarget/sandboxTarget.h
--- a/ipc/contentproc/plugin-container.cpp
+++ b/ipc/contentproc/plugin-container.cpp
@@ -149,26 +149,45 @@ MakeSandboxStarter()
 int
 content_process_main(int argc, char* argv[])
 {
     // Check for the absolute minimum number of args we need to move
     // forward here. We expect the last arg to be the child process type.
     if (argc < 1) {
       return 3;
     }
-    XRE_SetProcessType(argv[--argc]);
 
     bool isNuwa = false;
     for (int i = 1; i < argc; i++) {
         isNuwa |= strcmp(argv[i], "-nuwa") == 0;
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
         gIsSandboxEnabled |= strcmp(argv[i], "-sandbox") == 0;
 #endif
     }
 
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+    if (gIsSandboxEnabled) {
+        sandbox::TargetServices* target_service =
+            sandbox::SandboxFactory::GetTargetServices();
+        if (!target_service) {
+            return 1;
+        }
+
+        sandbox::ResultCode result = target_service->Init();
+        if (result != sandbox::SBOX_ALL_OK) {
+           return 2;
+        }
+
+        mozilla::SandboxTarget::Instance()->SetTargetServices(target_service);
+        mozilla::sandboxing::PrepareForLogging();
+    }
+#endif
+
+    XRE_SetProcessType(argv[--argc]);
+
 #ifdef MOZ_NUWA_PROCESS
     if (isNuwa) {
         PrepareNuwaProcess();
     }
 #endif
 
 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
     // This has to happen while we're still single-threaded, and on
@@ -200,34 +219,16 @@ content_process_main(int argc, char* arg
 #ifdef XP_WIN
     // For plugins, this is done in PluginProcessChild::Init, as we need to
     // avoid it for unsupported plugins.  See PluginProcessChild::Init for
     // the details.
     if (XRE_GetProcessType() != GeckoProcessType_Plugin) {
         mozilla::SanitizeEnvironmentVariables();
         SetDllDirectory(L"");
     }
-
-#ifdef MOZ_SANDBOX
-    if (gIsSandboxEnabled) {
-        sandbox::TargetServices* target_service =
-            sandbox::SandboxFactory::GetTargetServices();
-        if (!target_service) {
-            return 1;
-        }
-
-        sandbox::ResultCode result =
-            mozilla::SandboxTarget::Instance()->InitTargetServices(target_service);
-        if (result != sandbox::SBOX_ALL_OK) {
-           return 2;
-        }
-
-        mozilla::sandboxing::PrepareForLogging();
-    }
-#endif
 #endif
     nsAutoPtr<mozilla::gmp::GMPLoader> loader;
 #if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
     // On desktop, the GMPLoader lives in plugin-container, so that its
     // code can be covered by an EME/GMP vendor's voucher.
     nsAutoPtr<mozilla::gmp::SandboxStarter> starter(MakeSandboxStarter());
     if (XRE_GetProcessType() == GeckoProcessType_GMPlugin) {
         loader = mozilla::gmp::CreateGMPLoader(starter);
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -549,16 +549,60 @@ AddAppDirToCommandLine(std::vector<std::
         aCmdLine.push_back("-appdir");
         aCmdLine.push_back(path.get());
 #endif
       }
     }
   }
 }
 
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void
+MaybeAddNsprLogFileAccess(std::vector<std::wstring>& aAllowedFilesReadWrite)
+{
+  const char* nsprLogFileEnv = PR_GetEnv("NSPR_LOG_FILE");
+  if (!nsprLogFileEnv) {
+    return;
+  }
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR,
+                                       getter_AddRefs(file));
+  if (NS_FAILED(rv) || !file) {
+    NS_WARNING("Failed to get current working directory");
+    return;
+  }
+
+  nsDependentCString nsprLogFile(nsprLogFileEnv);
+  rv = file->AppendRelativeNativePath(nsprLogFile);
+  if (NS_FAILED(rv)) {
+    // Not a relative path, try it as an absolute one.
+    rv = file->InitWithNativePath(nsprLogFile);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+
+  nsAutoString resolvedFilePath;
+  rv = file->GetPath(resolvedFilePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+  }
+
+  // Update the environment variable as well as adding the rule, because the
+  // Chromium sandbox can only allow access to fully qualified file paths. This
+  // only affects the environment for the child process we're about to create,
+  // because this will get reset to the original value in PerformAsyncLaunch.
+  aAllowedFilesReadWrite.push_back(resolvedFilePath.get());
+  nsAutoCString resolvedEnvVar("NSPR_LOG_FILE=");
+  AppendUTF16toUTF8(resolvedFilePath, resolvedEnvVar);
+  PR_SetEnv(resolvedEnvVar.get());
+}
+#endif
+
 bool
 GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch)
 {
   // We rely on the fact that InitializeChannel() has already been processed
   // on the IO thread before this point is reached.
   if (!GetChannel()) {
     return false;
   }
@@ -886,16 +930,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
       break;
     case GeckoProcessType_Default:
     default:
       MOZ_CRASH("Bad process type in GeckoChildProcessHost");
       break;
   };
 
   if (shouldSandboxCurrentProcess) {
+    MaybeAddNsprLogFileAccess(mAllowedFilesReadWrite);
     for (auto it = mAllowedFilesRead.begin();
          it != mAllowedFilesRead.end();
          ++it) {
       mSandboxBroker.AllowReadFile(it->c_str());
     }
 
     for (auto it = mAllowedFilesReadWrite.begin();
          it != mAllowedFilesReadWrite.end();
--- a/security/sandbox/win/src/sandboxtarget/sandboxTarget.h
+++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h
@@ -6,16 +6,17 @@
 
 #ifndef __SECURITY_SANDBOX_SANDBOXTARGET_H__
 #define __SECURITY_SANDBOX_SANDBOXTARGET_H__
 
 #include <windows.h>
 
 #include "base/MissingBasicTypes.h"
 #include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/target_services.h"
 
 #ifdef TARGET_SANDBOX_EXPORTS
 #define TARGET_SANDBOX_EXPORT __declspec(dllexport)
 #else
 #define TARGET_SANDBOX_EXPORT __declspec(dllimport)
 #endif
 namespace mozilla {
 
@@ -30,32 +31,30 @@ public:
   {
     static SandboxTarget sb;
     return &sb;
   }
 
   /**
    * Used by the application to pass in the target services that provide certain
    * functions to the sandboxed code.
+   * The target services must already be initialized.
    *
-   * @param aTargetServices The target services that will be initialized and used
+   * @param aTargetServices The target services that will be used
    */
-  sandbox::ResultCode
-  InitTargetServices(sandbox::TargetServices* aTargetServices)
+  void SetTargetServices(sandbox::TargetServices* aTargetServices)
   {
     MOZ_ASSERT(aTargetServices);
     MOZ_ASSERT(!mTargetServices,
-               "Sandbox TargetServices must only be initialized once.");
+               "Sandbox TargetServices must only be set once.");
+    // We use process_state_ instead of InitCalled() here due to linking issues.
+    MOZ_ASSERT(aTargetServices->GetState()->process_state_ > 1,
+               "Sandbox TargetServices must already be initialized.");
 
-    sandbox::ResultCode result = aTargetServices->Init();
-    if (sandbox::SBOX_ALL_OK == result) {
-      mTargetServices = aTargetServices;
-    }
-
-    return result;
+    mTargetServices = aTargetServices;
   }
 
   /**
    * Called by the library that wants to "start" the sandbox, i.e. change to the
    * more secure delayed / lockdown policy.
    */
   void StartSandbox()
   {