Bug 1147911 Part 9: Ensure file read permissions for file content process on Windows. r=jimm, r=jld
☠☠ backed out by aed16e234d2e ☠ ☠
authorBob Owen <bobowencode@gmail.com>
Wed, 23 Nov 2016 13:36:59 +0000
changeset 324088 11a036eafea224ab9ff1fde88f72ab75768be98c
parent 324087 2a7569652090bc2ca88c358b1afba087d8a9d861
child 324089 ef0568a85aa40bd2a2a43c8c813c059451f3d328
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersjimm, jld
bugs1147911
milestone53.0a1
Bug 1147911 Part 9: Ensure file read permissions for file content process on Windows. r=jimm, r=jld
dom/ipc/ContentParent.cpp
ipc/chromium/moz.build
ipc/chromium/src/base/child_privileges.h
ipc/chromium/src/base/process_util.h
ipc/glue/GeckoChildProcessHost.cpp
security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
security/sandbox/win/src/sandboxbroker/sandboxBroker.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1812,17 +1812,19 @@ ContentParent::ContentParent(ContentPare
 #if defined(XP_WIN) && !defined(MOZ_B2G)
   // Request Windows message deferral behavior on our side of the PContent
   // channel. Generally only applies to the situation where we get caught in
   // a deadlock with the plugin process when sending CPOWs.
   GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  ChildPrivileges privs = base::PRIVILEGES_DEFAULT;
+  ChildPrivileges privs = mRemoteType.EqualsLiteral("file")
+                          ? base::PRIVILEGES_FILEREAD
+                          : base::PRIVILEGES_DEFAULT;
   mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs);
 }
 
 ContentParent::~ContentParent()
 {
   if (mForceKillTimer) {
     mForceKillTimer->Cancel();
   }
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -53,16 +53,21 @@ if os_win:
         'src/base/thread_local_win.cc',
         'src/base/time_win.cc',
         'src/base/waitable_event_win.cc',
         'src/base/win_util.cc',
         'src/chrome/common/ipc_channel_win.cc',
         'src/chrome/common/process_watcher_win.cc',
         'src/chrome/common/transport_dib_win.cc',
     ]
+
+    EXPORTS.base += [
+        'src/base/child_privileges.h',
+    ]
+
 elif not CONFIG['MOZ_SYSTEM_LIBEVENT']:
     DIRS += ['src/third_party']
 
 if os_posix:
     UNIFIED_SOURCES += [
         'src/base/condition_variable_posix.cc',
         'src/base/file_descriptor_shuffle.cc',
         'src/base/file_util_posix.cc',
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/base/child_privileges.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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/. */
+
+#ifndef BASE_CHILD_PRIVILEGS_H_
+#define BASE_CHILD_PRIVILEGS_H_
+
+namespace base {
+
+enum ChildPrivileges {
+  PRIVILEGES_DEFAULT,
+  PRIVILEGES_UNPRIVILEGED,
+  PRIVILEGES_INHERIT,
+  // PRIVILEGES_DEFAULT plus file read permissions, used for file content process.
+  PRIVILEGES_FILEREAD,
+  PRIVILEGES_LAST
+};
+
+} // namespace base
+
+#endif  // BASE_CHILD_PRIVILEGS_H_
--- a/ipc/chromium/src/base/process_util.h
+++ b/ipc/chromium/src/base/process_util.h
@@ -31,16 +31,17 @@
 #include <string>
 #include <vector>
 #include <stdio.h>
 #include <stdlib.h>
 #ifndef OS_WIN
 #include <unistd.h>
 #endif
 
+#include "base/child_privileges.h"
 #include "base/command_line.h"
 #include "base/process.h"
 
 #if defined(OS_WIN)
 typedef PROCESSENTRY32 ProcessEntry;
 typedef IO_COUNTERS IoCounters;
 #elif defined(OS_POSIX)
 // TODO(port): we should not rely on a Win32 structure.
@@ -138,23 +139,16 @@ ProcessId GetProcId(ProcessHandle proces
 // multi-threading.
 void SetAllFDsToCloseOnExec();
 // Close all file descriptors, expect those which are a destination in the
 // given multimap. Only call this function in a child process where you know
 // that there aren't any other threads.
 void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map);
 #endif
 
-enum ChildPrivileges {
-  PRIVILEGES_DEFAULT,
-  PRIVILEGES_UNPRIVILEGED,
-  PRIVILEGES_INHERIT,
-  PRIVILEGES_LAST
-};
-
 #if defined(OS_WIN)
 // Runs the given application name with the given command line. Normally, the
 // first command line argument should be the path to the process, and don't
 // forget to quote it.
 //
 // If wait is true, it will block and wait for the other process to finish,
 // otherwise, it will just continue asynchronously.
 //
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -756,17 +756,18 @@ GeckoChildProcessHost::PerformAsyncLaunc
   // std::wstring in code compiled with Mozilla's -fshort-wchar
   // configuration, because chromium is compiled with -fno-short-wchar
   // and passing wstrings from one config to the other is unsafe.  So
   // we split the logic here.
 
 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
   base::environment_map newEnvVars;
   ChildPrivileges privs = mPrivileges;
-  if (privs == base::PRIVILEGES_DEFAULT) {
+  if (privs == base::PRIVILEGES_DEFAULT ||
+      privs == base::PRIVILEGES_FILEREAD) {
     privs = DefaultChildPrivileges();
   }
 
 #if defined(MOZ_WIDGET_GTK)
   if (mProcessType == GeckoProcessType_Content) {
     // disable IM module to avoid sandbox violation
     newEnvVars["GTK_IM_MODULE"] = "gtk-im-context-simple";
   }
@@ -1049,17 +1050,18 @@ GeckoChildProcessHost::PerformAsyncLaunc
   switch (mProcessType) {
     case GeckoProcessType_Content:
 #if defined(MOZ_CONTENT_SANDBOX)
       if (mSandboxLevel > 0 &&
           !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
         // For now we treat every failure as fatal in SetSecurityLevelForContentProcess
         // and just crash there right away. Should this change in the future then we
         // should also handle the error here.
-        mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel);
+        mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel,
+                                                         mPrivileges);
         shouldSandboxCurrentProcess = true;
         AddContentSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead);
       }
 #endif // MOZ_CONTENT_SANDBOX
       break;
     case GeckoProcessType_Plugin:
       if (mSandboxLevel > 0 &&
           !PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -87,17 +87,18 @@ SandboxBroker::LaunchApp(const wchar_t *
   // Return the process handle to the caller
   *aProcessHandle = targetInfo.hProcess;
 
   return true;
 }
 
 #if defined(MOZ_CONTENT_SANDBOX)
 void
-SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel)
+SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+                                                 base::ChildPrivileges aPrivs)
 {
   MOZ_RELEASE_ASSERT(mPolicy, "mPolicy must be set before this call.");
 
   sandbox::JobLevel jobLevel;
   sandbox::TokenLevel accessTokenLevel;
   sandbox::IntegrityLevel initialIntegrityLevel;
   sandbox::IntegrityLevel delayedIntegrityLevel;
 
@@ -122,16 +123,26 @@ SandboxBroker::SetSecurityLevelForConten
     delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
   } else if (aSandboxLevel == 1) {
     jobLevel = sandbox::JOB_NONE;
     accessTokenLevel = sandbox::USER_NON_ADMIN;
     initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
     delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
   }
 
+  // If PRIVILEGES_FILEREAD required, don't allow settings that block reads.
+  if (aPrivs == base::ChildPrivileges::PRIVILEGES_FILEREAD) {
+    if (accessTokenLevel < sandbox::USER_NON_ADMIN) {
+      accessTokenLevel = sandbox::USER_NON_ADMIN;
+    }
+    if (delayedIntegrityLevel > sandbox::INTEGRITY_LEVEL_LOW) {
+      delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+    }
+  }
+
   sandbox::ResultCode result = mPolicy->SetJobLevel(jobLevel,
                                                     0 /* ui_exceptions */);
   MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
                      "Setting job level failed, have you set memory limit when jobLevel == JOB_NONE?");
 
   result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
                                   accessTokenLevel);
   MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __SECURITY_SANDBOX_SANDBOXBROKER_H__
 #define __SECURITY_SANDBOX_SANDBOXBROKER_H__
 
 #include <stdint.h>
 #include <windows.h>
 
+#include "base/child_privileges.h"
+
 namespace sandbox {
   class BrokerServices;
   class TargetPolicy;
 }
 
 namespace mozilla {
 
 class SandboxBroker
@@ -27,17 +29,18 @@ public:
   bool LaunchApp(const wchar_t *aPath,
                  const wchar_t *aArguments,
                  const bool aEnableLogging,
                  void **aProcessHandle);
   virtual ~SandboxBroker();
 
   // Security levels for different types of processes
 #if defined(MOZ_CONTENT_SANDBOX)
-  void SetSecurityLevelForContentProcess(int32_t aSandboxLevel);
+  void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+                                         base::ChildPrivileges aPrivs);
 #endif
   bool SetSecurityLevelForPluginProcess(int32_t aSandboxLevel);
   enum SandboxLevel {
     LockDown,
     Restricted
   };
   bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel);