summaryrefslogtreecommitdiffstats
path: root/security/sandbox/win
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/sandbox/win/SandboxInitialization.cpp202
-rw-r--r--security/sandbox/win/SandboxInitialization.h52
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl36
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp97
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h35
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp83
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h48
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp27
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h33
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp35
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h41
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/moz.build30
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp170
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h75
-rw-r--r--security/sandbox/win/src/sandboxbroker/moz.build21
-rw-r--r--security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp2029
-rw-r--r--security/sandbox/win/src/sandboxbroker/sandboxBroker.h137
-rw-r--r--security/sandbox/win/src/sandboxtarget/moz.build22
-rw-r--r--security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp64
-rw-r--r--security/sandbox/win/src/sandboxtarget/sandboxTarget.h80
20 files changed, 3317 insertions, 0 deletions
diff --git a/security/sandbox/win/SandboxInitialization.cpp b/security/sandbox/win/SandboxInitialization.cpp
new file mode 100644
index 0000000000..8ba8b4e69a
--- /dev/null
+++ b/security/sandbox/win/SandboxInitialization.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+#include "SandboxInitialization.h"
+
+#include "base/memory/ref_counted.h"
+#include "nsWindowsDllInterceptor.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/WindowsProcessMitigations.h"
+
+namespace mozilla {
+namespace sandboxing {
+
+typedef BOOL(WINAPI* CloseHandle_func)(HANDLE hObject);
+static WindowsDllInterceptor::FuncHookType<CloseHandle_func> stub_CloseHandle;
+
+typedef BOOL(WINAPI* DuplicateHandle_func)(
+ HANDLE hSourceProcessHandle, HANDLE hSourceHandle,
+ HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess,
+ BOOL bInheritHandle, DWORD dwOptions);
+static WindowsDllInterceptor::FuncHookType<DuplicateHandle_func>
+ stub_DuplicateHandle;
+
+static BOOL WINAPI patched_CloseHandle(HANDLE hObject) {
+ // Check all handles being closed against the sandbox's tracked handles.
+ base::win::OnHandleBeingClosed(hObject);
+ return stub_CloseHandle(hObject);
+}
+
+static BOOL WINAPI patched_DuplicateHandle(
+ HANDLE hSourceProcessHandle, HANDLE hSourceHandle,
+ HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess,
+ BOOL bInheritHandle, DWORD dwOptions) {
+ // If closing a source handle from our process check it against the sandbox's
+ // tracked handles.
+ if ((dwOptions & DUPLICATE_CLOSE_SOURCE) &&
+ (GetProcessId(hSourceProcessHandle) == ::GetCurrentProcessId())) {
+ base::win::OnHandleBeingClosed(hSourceHandle);
+ }
+
+ return stub_DuplicateHandle(hSourceProcessHandle, hSourceHandle,
+ hTargetProcessHandle, lpTargetHandle,
+ dwDesiredAccess, bInheritHandle, dwOptions);
+}
+
+typedef BOOL(WINAPI* ApiSetQueryApiSetPresence_func)(PCUNICODE_STRING,
+ PBOOLEAN);
+static WindowsDllInterceptor::FuncHookType<ApiSetQueryApiSetPresence_func>
+ stub_ApiSetQueryApiSetPresence;
+
+static const WCHAR gApiSetNtUserWindowStation[] =
+ L"ext-ms-win-ntuser-windowstation-l1-1-0";
+
+static BOOL WINAPI patched_ApiSetQueryApiSetPresence(
+ PCUNICODE_STRING aNamespace, PBOOLEAN aPresent) {
+ if (aNamespace && aPresent &&
+ !wcsncmp(aNamespace->Buffer, gApiSetNtUserWindowStation,
+ aNamespace->Length / sizeof(WCHAR))) {
+ *aPresent = FALSE;
+ return TRUE;
+ }
+
+ return stub_ApiSetQueryApiSetPresence(aNamespace, aPresent);
+}
+
+static WindowsDllInterceptor Kernel32Intercept;
+static WindowsDllInterceptor gApiQueryIntercept;
+
+static bool EnableHandleCloseMonitoring() {
+ Kernel32Intercept.Init("kernel32.dll");
+ bool hooked = stub_CloseHandle.Set(Kernel32Intercept, "CloseHandle",
+ &patched_CloseHandle);
+ if (!hooked) {
+ return false;
+ }
+
+ hooked = stub_DuplicateHandle.Set(Kernel32Intercept, "DuplicateHandle",
+ &patched_DuplicateHandle);
+ if (!hooked) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * There is a bug in COM that causes its initialization to fail when user32.dll
+ * is loaded but Win32k lockdown is enabled. COM uses ApiSetQueryApiSetPresence
+ * to make this check. When we are under Win32k lockdown, we hook
+ * ApiSetQueryApiSetPresence and force it to tell the caller that the DLL of
+ * interest is not present.
+ */
+static void EnableApiQueryInterception() {
+ if (!IsWin32kLockedDown()) {
+ return;
+ }
+
+ gApiQueryIntercept.Init(L"Api-ms-win-core-apiquery-l1-1-0.dll");
+ DebugOnly<bool> hookSetOk = stub_ApiSetQueryApiSetPresence.Set(
+ gApiQueryIntercept, "ApiSetQueryApiSetPresence",
+ &patched_ApiSetQueryApiSetPresence);
+ MOZ_ASSERT(hookSetOk);
+}
+
+static bool ShouldDisableHandleVerifier() {
+#if defined(_X86_) && (defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG))
+ // Chromium only has the verifier enabled for 32-bit and our close monitoring
+ // hooks cause debug assertions for 64-bit anyway.
+ // For x86 keep the verifier enabled by default only for Nightly or debug.
+ return false;
+#else
+ return !getenv("MOZ_ENABLE_HANDLE_VERIFIER");
+#endif
+}
+
+static void InitializeHandleVerifier() {
+ // Disable the handle verifier if we don't want it or can't enable the close
+ // monitoring hooks.
+ if (ShouldDisableHandleVerifier() || !EnableHandleCloseMonitoring()) {
+ base::win::DisableHandleVerifier();
+ }
+}
+
+static sandbox::TargetServices* InitializeTargetServices() {
+ // This might disable the verifier, so we want to do it before it is used.
+ InitializeHandleVerifier();
+
+ EnableApiQueryInterception();
+
+ sandbox::TargetServices* targetServices =
+ sandbox::SandboxFactory::GetTargetServices();
+ if (!targetServices) {
+ return nullptr;
+ }
+
+ if (targetServices->Init() != sandbox::SBOX_ALL_OK) {
+ return nullptr;
+ }
+
+ return targetServices;
+}
+
+sandbox::TargetServices* GetInitializedTargetServices() {
+ static sandbox::TargetServices* sInitializedTargetServices =
+ InitializeTargetServices();
+
+ return sInitializedTargetServices;
+}
+
+void LowerSandbox() { GetInitializedTargetServices()->LowerToken(); }
+
+static sandbox::BrokerServices* InitializeBrokerServices() {
+ // This might disable the verifier, so we want to do it before it is used.
+ InitializeHandleVerifier();
+
+ sandbox::BrokerServices* brokerServices =
+ sandbox::SandboxFactory::GetBrokerServices();
+ if (!brokerServices) {
+ return nullptr;
+ }
+
+ if (brokerServices->Init() != sandbox::SBOX_ALL_OK) {
+ return nullptr;
+ }
+
+ // Comment below copied from Chromium code.
+ // Precreate the desktop and window station used by the renderers.
+ // IMPORTANT: This piece of code needs to run as early as possible in the
+ // process because it will initialize the sandbox broker, which requires
+ // the process to swap its window station. During this time all the UI
+ // will be broken. This has to run before threads and windows are created.
+ scoped_refptr<sandbox::TargetPolicy> policy = brokerServices->CreatePolicy();
+ policy->CreateAlternateDesktop(true);
+
+ // Ensure the relevant mitigations are enforced.
+ mozilla::sandboxing::ApplyParentProcessMitigations();
+
+ return brokerServices;
+}
+
+sandbox::BrokerServices* GetInitializedBrokerServices() {
+ static sandbox::BrokerServices* sInitializedBrokerServices =
+ InitializeBrokerServices();
+
+ return sInitializedBrokerServices;
+}
+
+void ApplyParentProcessMitigations() {
+ // The main reason for this call is for the token hardening, but chromium code
+ // also ensures DEP without ATL thunk so we do the same.
+ sandbox::ApplyProcessMitigationsToCurrentProcess(
+ sandbox::MITIGATION_DEP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+ sandbox::MITIGATION_HARDEN_TOKEN_IL_POLICY);
+}
+
+} // namespace sandboxing
+} // namespace mozilla
diff --git a/security/sandbox/win/SandboxInitialization.h b/security/sandbox/win/SandboxInitialization.h
new file mode 100644
index 0000000000..1d49c0d899
--- /dev/null
+++ b/security/sandbox/win/SandboxInitialization.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; 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 mozilla_sandboxing_SandboxInitialization_h
+#define mozilla_sandboxing_SandboxInitialization_h
+
+namespace sandbox {
+class BrokerServices;
+class TargetServices;
+} // namespace sandbox
+
+// Things that use this file will probably want access to the IsSandboxedProcess
+// function defined in one of the Chromium sandbox cc files.
+extern "C" bool IsSandboxedProcess();
+
+namespace mozilla {
+// Note the Chromium code just uses a bare sandbox namespace, which makes using
+// sandbox for our namespace painful.
+namespace sandboxing {
+
+/**
+ * Initializes (if required) and returns the Chromium sandbox TargetServices.
+ *
+ * @return the TargetServices or null if the creation or initialization failed.
+ */
+sandbox::TargetServices* GetInitializedTargetServices();
+
+/**
+ * Lowers the permissions on the process sandbox.
+ * Provided because the GMP sandbox needs to be lowered from the executable.
+ */
+void LowerSandbox();
+
+/**
+ * Initializes (if required) and returns the Chromium sandbox BrokerServices.
+ *
+ * @return the BrokerServices or null if the creation or initialization failed.
+ */
+sandbox::BrokerServices* GetInitializedBrokerServices();
+
+/**
+ * Apply mitigations for parent processes.
+ */
+void ApplyParentProcessMitigations();
+
+} // namespace sandboxing
+} // namespace mozilla
+
+#endif // mozilla_sandboxing_SandboxInitialization_h
diff --git a/security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl b/security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl
new file mode 100644
index 0000000000..7057a2ca42
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
+
+namespace mozilla {
+
+struct EnvVar {
+ nsString name;
+ nsString value;
+};
+
+struct LaunchParameters {
+ nsString path;
+ nsString args;
+ EnvVar[] env;
+ uint32_t processType;
+ uint32_t sandboxLevel;
+ nsString[] allowedReadFiles;
+ uint64_t[] shareHandles;
+ bool enableLogging;
+};
+
+[NeedsOtherPid, NestedUpTo=inside_sync]
+sync protocol PRemoteSandboxBroker
+{
+parent:
+ async InitCrashReporter(NativeThreadId threadId);
+child:
+ [Nested=inside_sync] sync LaunchApp(LaunchParameters params)
+ returns (bool ok, uint64_t handle);
+};
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp
new file mode 100644
index 0000000000..0ce2a60a8e
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerChild.h"
+#include "chrome/common/ipc_channel.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "nsDebugImpl.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "RemoteSandboxBrokerProcessChild.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+
+RemoteSandboxBrokerChild::RemoteSandboxBrokerChild() {
+ nsDebugImpl::SetMultiprocessMode("RemoteSandboxBroker");
+}
+
+RemoteSandboxBrokerChild::~RemoteSandboxBrokerChild() {}
+
+bool RemoteSandboxBrokerChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint) {
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ return false;
+ }
+ CrashReporterClient::InitSingleton(this);
+ return true;
+}
+
+void RemoteSandboxBrokerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Abnormal shutdown of GMP process!");
+ ipc::ProcessChild::QuickExit();
+ }
+ CrashReporterClient::DestroySingleton();
+ XRE_ShutdownChildProcess();
+}
+
+mozilla::ipc::IPCResult RemoteSandboxBrokerChild::RecvLaunchApp(
+ LaunchParameters&& aParams, bool* aOutOk, uint64_t* aOutHandle) {
+ auto towstring = [](const nsString& s) {
+ return std::wstring(s.get(), s.Length());
+ };
+
+ base::EnvironmentMap envmap;
+ for (const EnvVar& env : aParams.env()) {
+ envmap[towstring(env.name())] = towstring(env.value());
+ }
+
+ // We need to add our parent as a target peer, so that the sandboxed child can
+ // duplicate handles to it for crash reporting. AddTargetPeer duplicates the
+ // handle, so we use a ScopedProcessHandle to automatically close ours.
+ ipc::ScopedProcessHandle parentProcHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &parentProcHandle.rwget())) {
+ *aOutOk = false;
+ return IPC_OK();
+ }
+ mSandboxBroker.AddTargetPeer(parentProcHandle);
+
+ if (!mSandboxBroker.SetSecurityLevelForGMPlugin(
+ AbstractSandboxBroker::SandboxLevel(aParams.sandboxLevel()),
+ /* aIsRemoteLaunch */ true)) {
+ *aOutOk = false;
+ return IPC_OK();
+ }
+
+ for (const auto& path : aParams.allowedReadFiles()) {
+ if (!mSandboxBroker.AllowReadFile(path.get())) {
+ *aOutOk = false;
+ return IPC_OK();
+ }
+ }
+
+ for (const auto& handle : aParams.shareHandles()) {
+ mSandboxBroker.AddHandleToShare(HANDLE(handle));
+ }
+
+ HANDLE p;
+ mozilla::Result<mozilla::Ok, LaunchError> err =
+ mSandboxBroker.LaunchApp(aParams.path().get(), aParams.args().get(),
+ envmap, GeckoProcessType(aParams.processType()),
+ aParams.enableLogging(), nullptr, (void**)&p);
+ *aOutOk = err.isOk();
+ if (*aOutOk) {
+ *aOutHandle = uint64_t(p);
+ }
+
+ for (const auto& handle : aParams.shareHandles()) {
+ CloseHandle(HANDLE(handle));
+ }
+
+ return IPC_OK();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h
new file mode 100644
index 0000000000..cc707609df
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef RemoteSandboxBrokerChild_h_
+#define RemoteSandboxBrokerChild_h_
+
+#include "mozilla/PRemoteSandboxBrokerChild.h"
+#include "sandboxBroker.h"
+
+namespace mozilla {
+
+class RemoteSandboxBrokerChild : public PRemoteSandboxBrokerChild {
+ friend class PRemoteSandboxBrokerChild;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(RemoteSandboxBrokerChild, override)
+
+ RemoteSandboxBrokerChild();
+ bool Init(mozilla::ipc::UntypedEndpoint&& aEndpoint);
+
+ private:
+ virtual ~RemoteSandboxBrokerChild();
+ mozilla::ipc::IPCResult RecvLaunchApp(LaunchParameters&& aParams,
+ bool* aOutOk, uint64_t* aOutHandle);
+
+ void ActorDestroy(ActorDestroyReason aWhy);
+ SandboxBroker mSandboxBroker;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp
new file mode 100644
index 0000000000..7306f4f7bf
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerParent.h"
+#include "RemoteSandboxBrokerProcessParent.h"
+#include "mozilla/Telemetry.h"
+#include <windows.h>
+
+namespace mozilla {
+
+RefPtr<GenericPromise> RemoteSandboxBrokerParent::Launch(
+ uint32_t aLaunchArch, const nsTArray<uint64_t>& aHandlesToShare,
+ nsISerialEventTarget* aThread) {
+ MOZ_ASSERT(!mProcess);
+ if (mProcess) {
+ // Don't re-init.
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ }
+
+ mProcess = new RemoteSandboxBrokerProcessParent();
+#ifdef ALLOW_GECKO_CHILD_PROCESS_ARCH
+ mProcess->SetLaunchArchitecture(aLaunchArch);
+#endif
+ for (uint64_t handle : aHandlesToShare) {
+ mProcess->AddHandleToShare(HANDLE(handle));
+ }
+
+ auto resolve = [self = RefPtr{this}](base::ProcessHandle handle) {
+ self->mOpened = self->mProcess->TakeInitialEndpoint().Bind(self);
+ if (!self->mOpened) {
+ self->mProcess->Destroy();
+ self->mProcess = nullptr;
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ }
+ return GenericPromise::CreateAndResolve(true, __func__);
+ };
+
+ auto reject = [self = RefPtr{this}]() {
+ NS_ERROR("failed to launch child in the parent");
+ if (self->mProcess) {
+ self->mProcess->Destroy();
+ self->mProcess = nullptr;
+ }
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ };
+
+ return mProcess->AsyncLaunch()->Then(aThread, __func__, std::move(resolve),
+ std::move(reject));
+}
+
+bool RemoteSandboxBrokerParent::DuplicateFromLauncher(HANDLE aLauncherHandle,
+ LPHANDLE aOurHandle) {
+ return ::DuplicateHandle(mProcess->GetChildProcessHandle(), aLauncherHandle,
+ ::GetCurrentProcess(), aOurHandle, 0, false,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+}
+
+void RemoteSandboxBrokerParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (AbnormalShutdown == aWhy) {
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
+ nsDependentCString(XRE_GeckoProcessTypeToString(
+ GeckoProcessType_RemoteSandboxBroker)),
+ 1);
+ GenerateCrashReport(OtherPid());
+ }
+ Shutdown();
+}
+
+void RemoteSandboxBrokerParent::Shutdown() {
+ if (mOpened) {
+ mOpened = false;
+ Close();
+ }
+ if (mProcess) {
+ mProcess->Destroy();
+ mProcess = nullptr;
+ }
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h
new file mode 100644
index 0000000000..7645b68923
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef RemoteSandboxBrokerParent_h_
+#define RemoteSandboxBrokerParent_h_
+
+#include "mozilla/PRemoteSandboxBrokerParent.h"
+#include "RemoteSandboxBrokerProcessParent.h"
+#include "mozilla/ipc/CrashReporterHelper.h"
+
+namespace mozilla {
+
+class RemoteSandboxBrokerParent
+ : public PRemoteSandboxBrokerParent,
+ public ipc::CrashReporterHelper<GeckoProcessType_RemoteSandboxBroker> {
+ friend class PRemoteSandboxBrokerParent;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(RemoteSandboxBrokerParent, override)
+
+ bool DuplicateFromLauncher(HANDLE aLauncherHandle, LPHANDLE aOurHandle);
+
+ void Shutdown();
+
+ // Asynchronously launches the launcher process.
+ // Note: we rely on the caller to keep this instance alive
+ // until this promise resolves.
+ // aThread is the thread to use to resolve the promise on if needed.
+ RefPtr<GenericPromise> Launch(uint32_t aLaunchArch,
+ const nsTArray<uint64_t>& aHandlesToShare,
+ nsISerialEventTarget* aThread);
+
+ private:
+ ~RemoteSandboxBrokerParent() = default;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ RemoteSandboxBrokerProcessParent* mProcess = nullptr;
+
+ bool mOpened = false;
+};
+
+} // namespace mozilla
+
+#endif // RemoteSandboxBrokerParent_h_
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp
new file mode 100644
index 0000000000..32e7d3b1f4
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerProcessChild.h"
+
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/BackgroundHangMonitor.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+
+RemoteSandboxBrokerProcessChild::~RemoteSandboxBrokerProcessChild() {}
+
+bool RemoteSandboxBrokerProcessChild::Init(int aArgc, char* aArgv[]) {
+ BackgroundHangMonitor::Startup();
+ return mSandboxBrokerChild->Init(TakeInitialEndpoint());
+}
+
+void RemoteSandboxBrokerProcessChild::CleanUp() {
+ BackgroundHangMonitor::Shutdown();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h
new file mode 100644
index 0000000000..7b47f1e828
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 RemoteSandboxBrokerProcessChild_h_
+#define RemoteSandboxBrokerProcessChild_h_
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "RemoteSandboxBrokerChild.h"
+
+namespace mozilla {
+
+class RemoteSandboxBrokerProcessChild final
+ : public mozilla::ipc::ProcessChild {
+ protected:
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+ public:
+ using ProcessChild::ProcessChild;
+ ~RemoteSandboxBrokerProcessChild();
+
+ bool Init(int aArgc, char* aArgv[]) override;
+ void CleanUp() override;
+
+ private:
+ RefPtr<RemoteSandboxBrokerChild> mSandboxBrokerChild =
+ new RemoteSandboxBrokerChild;
+};
+
+} // namespace mozilla
+
+#endif // GMPProcessChild_h_
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp
new file mode 100644
index 0000000000..218753bb52
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerProcessParent.h"
+#include <windows.h>
+
+#include "mozilla/ipc/LaunchError.h"
+
+using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::ipc::LaunchError;
+using mozilla::ipc::ProcessHandlePromise;
+
+namespace mozilla {
+
+RemoteSandboxBrokerProcessParent::RemoteSandboxBrokerProcessParent()
+ : GeckoChildProcessHost(GeckoProcessType_RemoteSandboxBroker) {
+ MOZ_COUNT_CTOR(RemoteSandboxBrokerProcessParent);
+}
+
+RemoteSandboxBrokerProcessParent::~RemoteSandboxBrokerProcessParent() {
+ MOZ_COUNT_DTOR(RemoteSandboxBrokerProcessParent);
+}
+
+RefPtr<ProcessHandlePromise> RemoteSandboxBrokerProcessParent::AsyncLaunch() {
+ if (!GeckoChildProcessHost::AsyncLaunch()) {
+ return ProcessHandlePromise::CreateAndReject(
+ LaunchError("RSBPP::AsyncLaunch"), __func__);
+ }
+ return WhenProcessHandleReady();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h
new file mode 100644
index 0000000000..6b298ee9c6
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=4 et :
+ * 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 RemoteSandboxBrokerProcessParent_h_
+#define RemoteSandboxBrokerProcessParent_h_
+
+#include "mozilla/Attributes.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/thread.h"
+#include "chrome/common/child_process_host.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+class nsIRunnable;
+
+namespace mozilla {
+
+class RemoteSandboxBrokerProcessParent final
+ : public mozilla::ipc::GeckoChildProcessHost {
+ public:
+ RemoteSandboxBrokerProcessParent();
+
+ RefPtr<ipc::ProcessHandlePromise> AsyncLaunch();
+
+ bool CanShutdown() override { return true; }
+
+ using mozilla::ipc::GeckoChildProcessHost::GetChannel;
+ using mozilla::ipc::GeckoChildProcessHost::GetChildProcessHandle;
+
+ private:
+ ~RemoteSandboxBrokerProcessParent();
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteSandboxBrokerProcessParent);
+};
+
+} // namespace mozilla
+
+#endif // ifndef GMPProcessParent_h
diff --git a/security/sandbox/win/src/remotesandboxbroker/moz.build b/security/sandbox/win/src/remotesandboxbroker/moz.build
new file mode 100644
index 0000000000..6d661ace4c
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/moz.build
@@ -0,0 +1,30 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ "remoteSandboxBroker.cpp",
+ "RemoteSandboxBrokerChild.cpp",
+ "RemoteSandboxBrokerParent.cpp",
+ "RemoteSandboxBrokerProcessChild.cpp",
+ "RemoteSandboxBrokerProcessParent.cpp",
+]
+
+EXPORTS.mozilla += [
+ "remoteSandboxBroker.h",
+ "RemoteSandboxBrokerChild.h",
+ "RemoteSandboxBrokerParent.h",
+ "RemoteSandboxBrokerProcessChild.h",
+ "RemoteSandboxBrokerProcessParent.h",
+]
+
+for var in ("UNICODE", "_UNICODE"):
+ DEFINES[var] = True
+
+FINAL_LIBRARY = "xul"
+
+IPDL_SOURCES += ["PRemoteSandboxBroker.ipdl"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp
new file mode 100644
index 0000000000..d1148e985a
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#include "remoteSandboxBroker.h"
+
+#include "RemoteSandboxBrokerParent.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsIThread.h"
+
+namespace mozilla {
+
+RemoteSandboxBroker::RemoteSandboxBroker(uint32_t aLaunchArch)
+ : mParent(new RemoteSandboxBrokerParent), mLaunchArch(aLaunchArch) {}
+
+RemoteSandboxBroker::~RemoteSandboxBroker() {
+ MOZ_ASSERT(
+ mShutdown,
+ "Shutdown must be called on RemoteSandboxBroker before destruction!");
+}
+
+void RemoteSandboxBroker::Shutdown() {
+ MOZ_ASSERT(!mShutdown, "Don't call Shutdown() twice!");
+ mShutdown = true;
+
+ if (!mIPCLaunchThread) {
+ // Can't have launched child process, nothing to shutdown.
+ return;
+ }
+
+ RefPtr<RemoteSandboxBroker> self = this;
+ mIPCLaunchThread->Dispatch(
+ NS_NewRunnableFunction("Remote Sandbox Launch", [self, this]() {
+ // Note: `self` here should be the last reference to this instance.
+ mParent->Shutdown();
+ mIPCLaunchThread = nullptr;
+ }));
+}
+
+Result<Ok, mozilla::ipc::LaunchError> RemoteSandboxBroker::LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA*, void** aProcessHandle) {
+ // Note: we expect to be called on the IPC launch thread from
+ // GeckoChildProcesHost while it's launching a child process. The IPC launch
+ // thread is a TaskQueue. We can't run a synchronous launch here as that
+ // blocks the calling thread while it dispatches a task to the IPC launch
+ // thread to spawn the process. Since our calling thread is the IPC launch
+ // thread, we'd then be deadlocked. So instead we do an async launch, and spin
+ // the event loop until the process launch succeeds.
+
+ // We should be on the IPC launch thread. We're shutdown on the IO thread,
+ // so save a ref to the IPC launch thread here, so we can close the channel
+ // on the IPC launch thread on shutdown.
+ mIPCLaunchThread = GetCurrentSerialEventTarget();
+
+ mParameters.path() = nsDependentString(aPath);
+ mParameters.args() = nsDependentString(aArguments);
+
+ auto toNsString = [](const std::wstring& s) {
+ return nsDependentString(s.c_str());
+ };
+ for (auto itr : aEnvironment) {
+ mParameters.env().AppendElement(
+ EnvVar(toNsString(itr.first), toNsString(itr.second)));
+ }
+
+ mParameters.processType() = uint32_t(aProcessType);
+ mParameters.enableLogging() = aEnableLogging;
+
+ enum Result { Pending, Succeeded, Failed };
+ Result res = Pending;
+ auto resolve = [&](bool ok) {
+ res = Succeeded;
+ return GenericPromise::CreateAndResolve(ok, __func__);
+ };
+
+ auto reject = [&](nsresult) {
+ res = Failed;
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ };
+
+ // We need to wait on the current thread for the process to launch which will
+ // block the running IPC Launch taskqueue. We cannot use
+ // GetCurrentSerialEventTarget() (as this returns the currently running
+ // TaskQueue) to resolve our promise as it will be blocked until we return
+ // from this function.
+ nsCOMPtr<nsISerialEventTarget> target = NS_GetCurrentThread();
+ mParent->Launch(mLaunchArch, mParameters.shareHandles(), target)
+ ->Then(target, __func__, std::move(resolve), std::move(reject));
+
+ // Spin the event loop while the sandbox launcher process launches.
+ SpinEventLoopUntil("RemoteSandboxBroker::LaunchApp"_ns,
+ [&]() { return res != Pending; });
+
+ if (res == Failed) {
+ return Err(mozilla::ipc::LaunchError("RSB::LaunchApp"));
+ }
+
+ uint64_t handle = 0;
+ bool ok = false;
+ bool rv = mParent->SendLaunchApp(std::move(mParameters), &ok, &handle) && ok;
+ mParameters.shareHandles().Clear();
+ if (!rv) {
+ mParent->Shutdown();
+ return Err(mozilla::ipc::LaunchError("RSB::SendLaunchApp"));
+ }
+
+ // Duplicate the handle of the child process that the launcher launched from
+ // the launcher process's space into this process' space.
+ HANDLE ourChildHandle = 0;
+ bool dh = mParent->DuplicateFromLauncher((HANDLE)handle, &ourChildHandle);
+ if (!dh) {
+ mParent->Shutdown();
+ return Err(mozilla::ipc::LaunchError("RSB::DuplicateFromLauncher"));
+ }
+
+ *aProcessHandle = (void**)(ourChildHandle);
+
+ base::ProcessHandle process = *aProcessHandle;
+ SandboxBroker::AddTargetPeer(process);
+
+ return Ok();
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch) {
+ mParameters.sandboxLevel() = uint32_t(aLevel);
+ return true;
+}
+
+bool RemoteSandboxBroker::AllowReadFile(wchar_t const* aFile) {
+ mParameters.allowedReadFiles().AppendElement(nsDependentString(aFile));
+ return true;
+}
+
+void RemoteSandboxBroker::AddHandleToShare(HANDLE aHandle) {
+ mParameters.shareHandles().AppendElement(uint64_t(aHandle));
+}
+
+void RemoteSandboxBroker::SetSecurityLevelForContentProcess(
+ int32_t aSandboxLevel, bool aIsFileProcess) {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForContentProcess not Implemented");
+}
+
+void RemoteSandboxBroker::SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForGPUProcess not Implemented");
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForRDDProcess() {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForRDDProcess not Implemented");
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForSocketProcess() {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForSocketProcess not Implemented");
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForUtilityProcess not Implemented");
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h
new file mode 100644
index 0000000000..bb02470e0e
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef __REMOTE_SANDBOXBROKER_H__
+#define __REMOTE_SANDBOXBROKER_H__
+
+#include "sandboxBroker.h"
+#include "RemoteSandboxBrokerParent.h"
+
+#include "mozilla/Result.h"
+#include "mozilla/ipc/LaunchError.h"
+
+namespace mozilla {
+
+// To make sandboxing an x86 plugin-container process on Windows on ARM64,
+// we launch an x86 child process which in turn launches and sandboxes the x86
+// plugin-container child. This means the sandbox broker (in the remote
+// x86 sandbox launcher process) can be same-arch with the process that it's
+// sandboxing, which means all the sandbox's assumptions about things being
+// same arch still hold.
+class RemoteSandboxBroker : public AbstractSandboxBroker {
+ public:
+ explicit RemoteSandboxBroker(uint32_t aLaunchArch);
+
+ void Shutdown() override;
+
+ // Note: This should be called on the IPC launch thread, and this spins
+ // the event loop. So this means potentially another IPC launch could occur
+ // re-entrantly while calling this.
+ Result<Ok, mozilla::ipc::LaunchError> LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA*,
+ void** aProcessHandle) override;
+
+ // Security levels for different types of processes
+ void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) override;
+ void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) override;
+ bool SetSecurityLevelForRDDProcess() override;
+ bool SetSecurityLevelForSocketProcess() override;
+ bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch = false) override;
+ bool SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) override;
+ bool AllowReadFile(wchar_t const* file) override;
+ void AddHandleToShare(HANDLE aHandle) override;
+
+ bool IsWin32kLockedDown() final { return false; };
+
+ private:
+ virtual ~RemoteSandboxBroker();
+
+ // Parameters that we use to launch the child process.
+ LaunchParameters mParameters;
+
+ RefPtr<RemoteSandboxBrokerParent> mParent;
+
+ // We bind the RemoteSandboxBrokerParent to the IPC launch thread.
+ // As such, we must close its channel on the same thread. So we save
+ // a reference to the IPC launch thread here.
+ nsCOMPtr<nsISerialEventTarget> mIPCLaunchThread;
+
+ // True if we've been shutdown.
+ bool mShutdown = false;
+
+ uint32_t mLaunchArch;
+};
+
+} // namespace mozilla
+
+#endif // __REMOTE_SANDBOXBROKER_H__
diff --git a/security/sandbox/win/src/sandboxbroker/moz.build b/security/sandbox/win/src/sandboxbroker/moz.build
new file mode 100644
index 0000000000..5eaf0de937
--- /dev/null
+++ b/security/sandbox/win/src/sandboxbroker/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ "sandboxBroker.cpp",
+]
+
+EXPORTS += [
+ "sandboxBroker.h",
+]
+
+for var in ("UNICODE", "_UNICODE"):
+ DEFINES[var] = True
+
+LOCAL_INCLUDES += ["/security/sandbox/chromium-shim"]
+LOCAL_INCLUDES += ["/security/sandbox/chromium"]
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
new file mode 100644
index 0000000000..bd154d3c78
--- /dev/null
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -0,0 +1,2029 @@
+/* -*- 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/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include "sandboxBroker.h"
+
+#include <aclapi.h>
+#include <shlobj.h>
+#include <string>
+
+#include "base/win/windows_version.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ImportDir.h"
+#include "mozilla/Logging.h"
+#include "mozilla/NSPRLogModulesParser.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SandboxSettings.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/StaticPrefs_widget.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/WinDllServices.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "mozilla/ipc/LaunchError.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsCOMPtr.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIProperties.h"
+#include "nsIXULRuntime.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/security_level.h"
+#include "WinUtils.h"
+
+namespace mozilla {
+
+constexpr wchar_t kLpacFirefoxInstallFiles[] = L"lpacFirefoxInstallFiles";
+
+sandbox::BrokerServices* sBrokerService = nullptr;
+
+// This is set to true in Initialize when our exe file name has a drive type of
+// DRIVE_REMOTE, so that we can tailor the sandbox policy as some settings break
+// fundamental things when running from a network drive. We default to false in
+// case those checks fail as that gives us the strongest policy.
+bool SandboxBroker::sRunningFromNetworkDrive = false;
+
+// Cached special directories used for adding policy rules.
+static UniquePtr<nsString> sBinDir;
+static UniquePtr<nsString> sProfileDir;
+static UniquePtr<nsString> sLocalAppDataDir;
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+static UniquePtr<nsString> sUserExtensionsDir;
+#endif
+
+static LazyLogModule sSandboxBrokerLog("SandboxBroker");
+
+#define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__))
+#define LOG_W(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Warning, (__VA_ARGS__))
+#define LOG_D(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Debug, (__VA_ARGS__))
+
+// Used to store whether we have accumulated an error combination for this
+// session.
+static UniquePtr<nsTHashtable<nsCStringHashKey>> sLaunchErrors;
+
+// This helper function is our version of SandboxWin::AddWin32kLockdownPolicy
+// of Chromium, making sure the MITIGATION_WIN32K_DISABLE flag is set before
+// adding the SUBSYS_WIN32K_LOCKDOWN rule which is required by
+// PolicyBase::AddRuleInternal.
+static sandbox::ResultCode AddWin32kLockdownPolicy(
+ sandbox::TargetPolicy* aPolicy, bool aEnableOpm) {
+ // On Windows 7, where Win32k lockdown is not supported, the Chromium
+ // sandbox does something weird that breaks COM instantiation.
+ if (!IsWin8OrLater()) {
+ return sandbox::SBOX_ALL_OK;
+ }
+
+ sandbox::MitigationFlags flags = aPolicy->GetProcessMitigations();
+ MOZ_ASSERT(flags,
+ "Mitigations should be set before AddWin32kLockdownPolicy.");
+ MOZ_ASSERT(!(flags & sandbox::MITIGATION_WIN32K_DISABLE),
+ "Check not enabling twice. Should not happen.");
+
+ flags |= sandbox::MITIGATION_WIN32K_DISABLE;
+ sandbox::ResultCode result = aPolicy->SetProcessMitigations(flags);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ result =
+ aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN,
+ aEnableOpm ? sandbox::TargetPolicy::IMPLEMENT_OPM_APIS
+ : sandbox::TargetPolicy::FAKE_USER_GDI_INIT,
+ nullptr);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+ if (aEnableOpm) {
+ aPolicy->SetEnableOPMRedirection();
+ }
+
+ return result;
+}
+
+/* static */
+void SandboxBroker::Initialize(sandbox::BrokerServices* aBrokerServices) {
+ sBrokerService = aBrokerServices;
+
+ sRunningFromNetworkDrive = widget::WinUtils::RunningFromANetworkDrive();
+}
+
+static void CacheDirAndAutoClear(nsIProperties* aDirSvc, const char* aDirKey,
+ UniquePtr<nsString>* cacheVar) {
+ nsCOMPtr<nsIFile> dirToCache;
+ nsresult rv =
+ aDirSvc->Get(aDirKey, NS_GET_IID(nsIFile), getter_AddRefs(dirToCache));
+ if (NS_FAILED(rv)) {
+ // This can only be an NS_WARNING, because it can fail for xpcshell tests.
+ NS_WARNING("Failed to get directory to cache.");
+ LOG_E("Failed to get directory to cache, key: %s.", aDirKey);
+ return;
+ }
+
+ *cacheVar = MakeUnique<nsString>();
+ ClearOnShutdown(cacheVar);
+ MOZ_ALWAYS_SUCCEEDS(dirToCache->GetPath(**cacheVar));
+
+ // Convert network share path to format for sandbox policy.
+ if (Substring(**cacheVar, 0, 2).Equals(u"\\\\"_ns)) {
+ (*cacheVar)->InsertLiteral(u"??\\UNC", 1);
+ }
+}
+
+/* static */
+void SandboxBroker::GeckoDependentInitialize() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool haveXPCOM = XRE_GetProcessType() != GeckoProcessType_RemoteSandboxBroker;
+ if (haveXPCOM) {
+ // Cache directory paths for use in policy rules, because the directory
+ // service must be called on the main thread.
+ nsresult rv;
+ nsCOMPtr<nsIProperties> dirSvc =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false,
+ "Failed to get directory service, cannot cache directories "
+ "for rules.");
+ LOG_E(
+ "Failed to get directory service, cannot cache directories for "
+ "rules.");
+ return;
+ }
+
+ CacheDirAndAutoClear(dirSvc, NS_GRE_DIR, &sBinDir);
+ CacheDirAndAutoClear(dirSvc, NS_APP_USER_PROFILE_50_DIR, &sProfileDir);
+ CacheDirAndAutoClear(dirSvc, NS_WIN_LOCAL_APPDATA_DIR, &sLocalAppDataDir);
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ CacheDirAndAutoClear(dirSvc, XRE_USER_SYS_EXTENSION_DIR,
+ &sUserExtensionsDir);
+#endif
+ }
+
+ // Create sLaunchErrors up front because ClearOnShutdown must be called on the
+ // main thread.
+ sLaunchErrors = MakeUnique<nsTHashtable<nsCStringHashKey>>();
+ ClearOnShutdown(&sLaunchErrors);
+}
+
+SandboxBroker::SandboxBroker() {
+ if (sBrokerService) {
+ scoped_refptr<sandbox::TargetPolicy> policy =
+ sBrokerService->CreatePolicy();
+ mPolicy = policy.get();
+ mPolicy->AddRef();
+ if (sRunningFromNetworkDrive) {
+ mPolicy->SetDoNotUseRestrictingSIDs();
+ }
+ } else {
+ mPolicy = nullptr;
+ }
+}
+
+#define WSTRING(STRING) L"" STRING
+
+static void AddMozLogRulesToPolicy(sandbox::TargetPolicy* aPolicy,
+ const base::EnvironmentMap& aEnvironment) {
+ auto it = aEnvironment.find(ENVIRONMENT_LITERAL("MOZ_LOG_FILE"));
+ if (it == aEnvironment.end()) {
+ it = aEnvironment.find(ENVIRONMENT_LITERAL("NSPR_LOG_FILE"));
+ }
+ if (it == aEnvironment.end()) {
+ return;
+ }
+
+ char const* logFileModules = getenv("MOZ_LOG");
+ if (!logFileModules) {
+ return;
+ }
+
+ // MOZ_LOG files have a standard file extension appended.
+ std::wstring logFileName(it->second);
+ logFileName.append(WSTRING(MOZ_LOG_FILE_EXTENSION));
+
+ // Allow for rotation number if rotate is on in the MOZ_LOG settings.
+ bool rotate = false;
+ NSPRLogModulesParser(
+ logFileModules,
+ [&rotate](const char* aName, LogLevel aLevel, int32_t aValue) {
+ if (strcmp(aName, "rotate") == 0) {
+ // Less or eq zero means to turn rotate off.
+ rotate = aValue > 0;
+ }
+ });
+ if (rotate) {
+ logFileName.append(L".?");
+ }
+
+ // Allow for %PID token in the filename. We don't allow it in the dir path, if
+ // specified, because we have to use a wildcard as we don't know the PID yet.
+ auto pidPos = logFileName.find(WSTRING(MOZ_LOG_PID_TOKEN));
+ auto lastSlash = logFileName.find_last_of(L"/\\");
+ if (pidPos != std::wstring::npos &&
+ (lastSlash == std::wstring::npos || lastSlash < pidPos)) {
+ logFileName.replace(pidPos, strlen(MOZ_LOG_PID_TOKEN), L"*");
+ }
+
+ aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY, logFileName.c_str());
+}
+
+static void AddDeveloperRepoDirToPolicy(sandbox::TargetPolicy* aPolicy) {
+ const wchar_t* developer_repo_dir =
+ _wgetenv(WSTRING("MOZ_DEVELOPER_REPO_DIR"));
+ if (!developer_repo_dir) {
+ return;
+ }
+
+ std::wstring repoPath(developer_repo_dir);
+ std::replace(repoPath.begin(), repoPath.end(), '/', '\\');
+ repoPath.append(WSTRING("\\*"));
+
+ aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ repoPath.c_str());
+}
+
+#undef WSTRING
+
+Result<Ok, mozilla::ipc::LaunchError> SandboxBroker::LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA* aCachedNtdllThunk,
+ void** aProcessHandle) {
+ if (!sBrokerService) {
+ return Err(mozilla::ipc::LaunchError("SB::LA::sBrokerService"));
+ }
+
+ if (!mPolicy) {
+ return Err(mozilla::ipc::LaunchError("SB::LA::mPolicy"));
+ }
+
+ // Set stdout and stderr, to allow inheritance for logging.
+ mPolicy->SetStdoutHandle(::GetStdHandle(STD_OUTPUT_HANDLE));
+ mPolicy->SetStderrHandle(::GetStdHandle(STD_ERROR_HANDLE));
+
+ // If logging enabled, set up the policy.
+ if (aEnableLogging) {
+ ApplyLoggingPolicy();
+ }
+
+#if defined(DEBUG)
+ // Allow write access to TEMP directory in debug builds for logging purposes.
+ // The path from GetTempPathW can have a length up to MAX_PATH + 1, including
+ // the null, so we need MAX_PATH + 2, so we can add an * to the end.
+ wchar_t tempPath[MAX_PATH + 2];
+ uint32_t pathLen = ::GetTempPathW(MAX_PATH + 1, tempPath);
+ if (pathLen > 0) {
+ // GetTempPath path ends with \ and returns the length without the null.
+ tempPath[pathLen] = L'*';
+ tempPath[pathLen + 1] = L'\0';
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY, tempPath);
+ }
+#endif
+
+ // Enable the child process to write log files when setup
+ AddMozLogRulesToPolicy(mPolicy, aEnvironment);
+
+ if (!mozilla::IsPackagedBuild()) {
+ AddDeveloperRepoDirToPolicy(mPolicy);
+ }
+
+ // Create the sandboxed process
+ PROCESS_INFORMATION targetInfo = {0};
+ sandbox::ResultCode result;
+ sandbox::ResultCode last_warning = sandbox::SBOX_ALL_OK;
+ DWORD last_error = ERROR_SUCCESS;
+ result = sBrokerService->SpawnTarget(aPath, aArguments, aEnvironment, mPolicy,
+ &last_warning, &last_error, &targetInfo);
+ if (sandbox::SBOX_ALL_OK != result) {
+ nsAutoCString key;
+ key.AppendASCII(XRE_GeckoProcessTypeToString(aProcessType));
+ key.AppendLiteral("/0x");
+ key.AppendInt(static_cast<uint32_t>(last_error), 16);
+
+ // Only accumulate for each combination once per session.
+ if (sLaunchErrors) {
+ if (!sLaunchErrors->Contains(key)) {
+ Telemetry::Accumulate(Telemetry::SANDBOX_FAILED_LAUNCH_KEYED, key,
+ result);
+ sLaunchErrors->PutEntry(key);
+ }
+ } else {
+ // If sLaunchErrors not created yet then always accumulate.
+ Telemetry::Accumulate(Telemetry::SANDBOX_FAILED_LAUNCH_KEYED, key,
+ result);
+ }
+
+ LOG_E(
+ "Failed (ResultCode %d) to SpawnTarget with last_error=%lu, "
+ "last_warning=%d",
+ result, last_error, last_warning);
+
+ return Err(mozilla::ipc::LaunchError("SB::LA::SpawnTarget", last_error));
+ } else if (sandbox::SBOX_ALL_OK != last_warning) {
+ // If there was a warning (but the result was still ok), log it and proceed.
+ LOG_W("Warning on SpawnTarget with last_error=%lu, last_warning=%d",
+ last_error, last_warning);
+ }
+
+#ifdef MOZ_THUNDERBIRD
+ // In Thunderbird, mInitDllBlocklistOOP is null, so InitDllBlocklistOOP would
+ // hit MOZ_RELEASE_ASSERT.
+ constexpr bool isThunderbird = true;
+#else
+ constexpr bool isThunderbird = false;
+#endif
+
+ if (!isThunderbird &&
+ XRE_GetChildProcBinPathType(aProcessType) == BinPathType::Self) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ LauncherVoidResultWithLineInfo blocklistInitOk =
+ dllSvc->InitDllBlocklistOOP(aPath, targetInfo.hProcess,
+ aCachedNtdllThunk, aProcessType);
+ if (blocklistInitOk.isErr()) {
+ dllSvc->HandleLauncherError(blocklistInitOk.unwrapErr(),
+ XRE_GeckoProcessTypeToString(aProcessType));
+ LOG_E("InitDllBlocklistOOP failed at %s:%d with HRESULT 0x%08lX",
+ blocklistInitOk.unwrapErr().mFile,
+ blocklistInitOk.unwrapErr().mLine,
+ blocklistInitOk.unwrapErr().mError.AsHResult());
+ TerminateProcess(targetInfo.hProcess, 1);
+ CloseHandle(targetInfo.hThread);
+ CloseHandle(targetInfo.hProcess);
+ return Err(mozilla::ipc::LaunchError(
+ "InitDllBlocklistOOP",
+ blocklistInitOk.unwrapErr().mError.AsHResult()));
+ }
+ } else {
+ // Load the child executable as a datafile so that we can examine its
+ // headers without doing a full load with dependencies and such.
+ nsModuleHandle moduleHandle(
+ ::LoadLibraryExW(aPath, nullptr, LOAD_LIBRARY_AS_DATAFILE));
+ if (moduleHandle) {
+ nt::CrossExecTransferManager transferMgr(targetInfo.hProcess,
+ moduleHandle);
+ if (!!transferMgr) {
+ LauncherVoidResult importsRestored =
+ RestoreImportDirectory(aPath, transferMgr);
+ if (importsRestored.isErr()) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->HandleLauncherError(
+ importsRestored.unwrapErr(),
+ XRE_GeckoProcessTypeToString(aProcessType));
+ LOG_E("Failed to restore import directory with HRESULT 0x%08lX",
+ importsRestored.unwrapErr().mError.AsHResult());
+ TerminateProcess(targetInfo.hProcess, 1);
+ CloseHandle(targetInfo.hThread);
+ CloseHandle(targetInfo.hProcess);
+ return Err(mozilla::ipc::LaunchError(
+ "RestoreImportDirectory",
+ importsRestored.unwrapErr().mError.AsHResult()));
+ }
+ }
+ }
+ }
+
+ // The sandboxed process is started in a suspended state, resume it now that
+ // we've set things up.
+ ResumeThread(targetInfo.hThread);
+ CloseHandle(targetInfo.hThread);
+
+ // Return the process handle to the caller
+ *aProcessHandle = targetInfo.hProcess;
+
+ return Ok();
+}
+
+static void AddCachedDirRule(sandbox::TargetPolicy* aPolicy,
+ sandbox::TargetPolicy::Semantics aAccess,
+ const UniquePtr<nsString>& aBaseDir,
+ const nsLiteralString& aRelativePath) {
+ if (!aBaseDir) {
+ // This can only be an NS_WARNING, because it can null for xpcshell tests.
+ NS_WARNING("Tried to add rule with null base dir.");
+ LOG_E("Tried to add rule with null base dir. Relative path: %S, Access: %d",
+ static_cast<const wchar_t*>(aRelativePath.get()), aAccess);
+ return;
+ }
+
+ nsAutoString rulePath(*aBaseDir);
+ rulePath.Append(aRelativePath);
+
+ sandbox::ResultCode result = aPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_FILES, aAccess, rulePath.get());
+ if (sandbox::SBOX_ALL_OK != result) {
+ NS_ERROR("Failed to add file policy rule.");
+ LOG_E("Failed (ResultCode %d) to add %d access to: %S", result, aAccess,
+ static_cast<const wchar_t*>(rulePath.get()));
+ }
+}
+
+// This function caches and returns an array of NT paths of the executable's
+// dependent modules.
+// If this returns Nothing(), it means the retrieval of the modules failed
+// (e.g. when the launcher process is disabled), so the process should not
+// enable pre-spawn CIG.
+static const Maybe<Vector<const wchar_t*>>& GetPrespawnCigExceptionModules() {
+ // We enable pre-spawn CIG only in Nightly for now
+ // because it caused a compat issue (bug 1682304 and 1704373).
+#if defined(NIGHTLY_BUILD)
+ // The shared section contains a list of dependent modules as a
+ // null-delimited string. We convert it to a string vector and
+ // cache it to avoid converting the same data every time.
+ static Maybe<Vector<const wchar_t*>> sDependentModules =
+ []() -> Maybe<Vector<const wchar_t*>> {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ auto sharedSection = dllSvc->GetSharedSection();
+ if (!sharedSection) {
+ return Nothing();
+ }
+
+ Span<const wchar_t> dependentModules = sharedSection->GetDependentModules();
+ if (dependentModules.IsEmpty()) {
+ return Nothing();
+ }
+
+ // Convert a null-delimited string set to a string vector.
+ Vector<const wchar_t*> paths;
+ for (const wchar_t* p = dependentModules.data();
+ (p - dependentModules.data() <
+ static_cast<long long>(dependentModules.size()) &&
+ *p);) {
+ Unused << paths.append(p);
+ while (*p) {
+ ++p;
+ }
+ ++p;
+ }
+
+ return Some(std::move(paths));
+ }();
+
+ return sDependentModules;
+#else
+ static const Maybe<Vector<const wchar_t*>> sNothing = Nothing();
+ return sNothing;
+#endif
+}
+
+static sandbox::ResultCode AllowProxyLoadFromBinDir(
+ sandbox::TargetPolicy* aPolicy) {
+ // Allow modules in the directory containing the executable such as
+ // mozglue.dll, nss3.dll, etc.
+ static UniquePtr<nsString> sInstallDir;
+ if (!sInstallDir) {
+ // Since this function can be called before sBinDir is initialized,
+ // we cache the install path by ourselves.
+ UniquePtr<wchar_t[]> appDirStr;
+ if (GetInstallDirectory(appDirStr)) {
+ sInstallDir = MakeUnique<nsString>(appDirStr.get());
+ sInstallDir->Append(u"\\*");
+
+ auto setClearOnShutdown = [ptr = &sInstallDir]() -> void {
+ ClearOnShutdown(ptr);
+ };
+ if (NS_IsMainThread()) {
+ setClearOnShutdown();
+ } else {
+ SchedulerGroup::Dispatch(
+ TaskCategory::Other,
+ NS_NewRunnableFunction("InitSignedPolicyRulesToBypassCig",
+ std::move(setClearOnShutdown)));
+ }
+ }
+
+ if (!sInstallDir) {
+ return sandbox::SBOX_ERROR_GENERIC;
+ }
+ }
+ return aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SIGNED_BINARY,
+ sandbox::TargetPolicy::SIGNED_ALLOW_LOAD,
+ sInstallDir->get());
+}
+
+static sandbox::ResultCode AddCigToPolicy(
+ sandbox::TargetPolicy* aPolicy, bool aAlwaysProxyBinDirLoading = false) {
+ const Maybe<Vector<const wchar_t*>>& exceptionModules =
+ GetPrespawnCigExceptionModules();
+ if (exceptionModules.isNothing()) {
+ sandbox::MitigationFlags delayedMitigations =
+ aPolicy->GetDelayedProcessMitigations();
+ MOZ_ASSERT(delayedMitigations,
+ "Delayed mitigations should be set before AddCigToPolicy.");
+ MOZ_ASSERT(!(delayedMitigations & sandbox::MITIGATION_FORCE_MS_SIGNED_BINS),
+ "AddCigToPolicy should not be called twice.");
+
+ delayedMitigations |= sandbox::MITIGATION_FORCE_MS_SIGNED_BINS;
+ sandbox::ResultCode result =
+ aPolicy->SetDelayedProcessMitigations(delayedMitigations);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ if (aAlwaysProxyBinDirLoading) {
+ result = AllowProxyLoadFromBinDir(aPolicy);
+ }
+ return result;
+ }
+
+ sandbox::MitigationFlags mitigations = aPolicy->GetProcessMitigations();
+ MOZ_ASSERT(mitigations, "Mitigations should be set before AddCigToPolicy.");
+ MOZ_ASSERT(!(mitigations & sandbox::MITIGATION_FORCE_MS_SIGNED_BINS),
+ "AddCigToPolicy should not be called twice.");
+
+ mitigations |= sandbox::MITIGATION_FORCE_MS_SIGNED_BINS;
+ sandbox::ResultCode result = aPolicy->SetProcessMitigations(mitigations);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ result = AllowProxyLoadFromBinDir(aPolicy);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ for (const wchar_t* path : exceptionModules.ref()) {
+ result = aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SIGNED_BINARY,
+ sandbox::TargetPolicy::SIGNED_ALLOW_LOAD, path);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+ }
+
+ return sandbox::SBOX_ALL_OK;
+}
+
+// Checks whether we can use a job object as part of the sandbox.
+static bool CanUseJob() {
+ // Windows 8 and later allows nested jobs, no need for further checks.
+ if (IsWin8OrLater()) {
+ return true;
+ }
+
+ BOOL inJob = true;
+ // If we can't determine if we are in a job then assume we can use one.
+ if (!::IsProcessInJob(::GetCurrentProcess(), nullptr, &inJob)) {
+ return true;
+ }
+
+ // If there is no job then we are fine to use one.
+ if (!inJob) {
+ return true;
+ }
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {};
+ // If we can't get the job object flags then again assume we can use a job.
+ if (!::QueryInformationJobObject(nullptr, JobObjectExtendedLimitInformation,
+ &job_info, sizeof(job_info), nullptr)) {
+ return true;
+ }
+
+ // If we can break away from the current job then we are free to set our own.
+ if (job_info.BasicLimitInformation.LimitFlags &
+ JOB_OBJECT_LIMIT_BREAKAWAY_OK) {
+ return true;
+ }
+
+ // Chromium added a command line flag to allow no job to be used, which was
+ // originally supposed to only be used for remote sessions. If you use runas
+ // to start Firefox then this also uses a separate job and we would fail to
+ // start on Windows 7. An unknown number of people use (or used to use) runas
+ // with Firefox for some security benefits (see bug 1228880). This is now a
+ // counterproductive technique, but allowing both the remote and local case
+ // for now and adding telemetry to see if we can restrict this to just remote.
+ nsAutoString localRemote(::GetSystemMetrics(SM_REMOTESESSION) ? u"remote"
+ : u"local");
+ Telemetry::ScalarSet(Telemetry::ScalarID::SANDBOX_NO_JOB, localRemote, true);
+
+ // Allow running without the job object in this case. This slightly reduces
+ // the ability of the sandbox to protect its children from spawning new
+ // processes or preventing them from shutting down Windows or accessing the
+ // clipboard.
+ return false;
+}
+
+// Returns the most strict dynamic code mitigation flag that is compatible with
+// system libraries MSAudDecMFT.dll and msmpeg2vdec.dll. This depends on the
+// Windows version and the architecture. See bug 1783223 comment 27.
+//
+// Use the result with SetDelayedProcessMitigations. Using non-delayed ACG
+// results in incompatibility with third-party antivirus software, the Windows
+// internal Shim Engine mechanism, parts of our own DLL blocklist code, and
+// AddressSanitizer initialization code. See bug 1783223.
+static sandbox::MitigationFlags DynamicCodeFlagForSystemMediaLibraries() {
+ static auto dynamicCodeFlag = []() {
+#ifdef _M_X64
+ if (IsWin10CreatorsUpdateOrLater()) {
+ return sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+ }
+#endif // _M_X64
+
+ if (IsWin10AnniversaryUpdateOrLater()) {
+ return sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT;
+ }
+
+ return sandbox::MitigationFlags{};
+ }();
+ return dynamicCodeFlag;
+}
+
+static sandbox::ResultCode SetJobLevel(sandbox::TargetPolicy* aPolicy,
+ sandbox::JobLevel aJobLevel,
+ uint32_t aUiExceptions) {
+ static bool sCanUseJob = CanUseJob();
+ if (sCanUseJob) {
+ return aPolicy->SetJobLevel(aJobLevel, aUiExceptions);
+ }
+
+ return aPolicy->SetJobLevel(sandbox::JOB_NONE, 0);
+}
+
+static void HexEncode(const Span<const uint8_t>& aBytes, nsACString& aEncoded) {
+ static const char kHexChars[] = "0123456789abcdef";
+
+ // Each input byte creates two output hex characters.
+ char* encodedPtr;
+ aEncoded.GetMutableData(&encodedPtr, aBytes.size() * 2);
+
+ for (auto byte : aBytes) {
+ *(encodedPtr++) = kHexChars[byte >> 4];
+ *(encodedPtr++) = kHexChars[byte & 0xf];
+ }
+}
+
+// This is left as a void because we might fail to set the permission for some
+// reason and yet the LPAC permission is already granted. So returning success
+// or failure isn't really that useful.
+static void EnsureLpacPermsissionsOnBinDir() {
+ BYTE sidBytes[SECURITY_MAX_SID_SIZE];
+ PSID lpacFirefoxInstallFilesSid = static_cast<PSID>(sidBytes);
+ if (!sBrokerService->DeriveCapabilitySidFromName(kLpacFirefoxInstallFiles,
+ lpacFirefoxInstallFilesSid,
+ sizeof(sidBytes))) {
+ LOG_E("Failed to derive Firefox install files capability SID.");
+ return;
+ }
+
+ HANDLE hBinDir =
+ ::CreateFileW(sBinDir->get(), WRITE_DAC | READ_CONTROL, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (hBinDir == INVALID_HANDLE_VALUE) {
+ LOG_W("Unable to get binary directory handle.");
+ return;
+ }
+
+ UniquePtr<HANDLE, CloseHandleDeleter> autoHandleCloser(hBinDir);
+ PACL pBinDirAcl = nullptr;
+ PSECURITY_DESCRIPTOR pSD = nullptr;
+ DWORD result =
+ ::GetSecurityInfo(hBinDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+ nullptr, nullptr, &pBinDirAcl, nullptr, &pSD);
+ if (result != ERROR_SUCCESS) {
+ LOG_E("Failed to get DACL for binary directory.");
+ return;
+ }
+
+ UniquePtr<VOID, LocalFreeDeleter> autoFreeSecDesc(pSD);
+ if (!pBinDirAcl) {
+ LOG_E("DACL for binary directory was null.");
+ return;
+ }
+
+ for (DWORD i = 0; i < pBinDirAcl->AceCount; ++i) {
+ VOID* pAce = nullptr;
+ if (!::GetAce(pBinDirAcl, i, &pAce) ||
+ static_cast<PACE_HEADER>(pAce)->AceType != ACCESS_ALLOWED_ACE_TYPE) {
+ continue;
+ }
+
+ auto* pAllowedAce = static_cast<ACCESS_ALLOWED_ACE*>(pAce);
+ if ((pAllowedAce->Mask & (GENERIC_READ | GENERIC_EXECUTE)) !=
+ (GENERIC_READ | GENERIC_EXECUTE)) {
+ continue;
+ }
+
+ PSID aceSID = reinterpret_cast<PSID>(&(pAllowedAce->SidStart));
+ if (::EqualSid(aceSID, lpacFirefoxInstallFilesSid)) {
+ LOG_D("Firefox install files permission found on binary directory.");
+ return;
+ }
+ }
+
+ EXPLICIT_ACCESS_W newAccess = {0};
+ newAccess.grfAccessMode = GRANT_ACCESS;
+ newAccess.grfAccessPermissions = GENERIC_READ | GENERIC_EXECUTE;
+ newAccess.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ ::BuildTrusteeWithSidW(&newAccess.Trustee, lpacFirefoxInstallFilesSid);
+ PACL newDacl = nullptr;
+ if (ERROR_SUCCESS !=
+ ::SetEntriesInAclW(1, &newAccess, pBinDirAcl, &newDacl)) {
+ LOG_E("Failed to create new DACL with Firefox install files SID.");
+ return;
+ }
+
+ UniquePtr<ACL, LocalFreeDeleter> autoFreeAcl(newDacl);
+ if (ERROR_SUCCESS != ::SetSecurityInfo(hBinDir, SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION, nullptr,
+ nullptr, newDacl, nullptr)) {
+ LOG_E("Failed to set new DACL on binary directory.");
+ }
+
+ LOG_D("Firefox install files permission granted on binary directory.");
+}
+
+static bool IsLowPrivilegedAppContainerSupported() {
+ // Chromium doesn't support adding an LPAC before this version due to
+ // incompatibility with some process mitigations.
+ return IsWin10Sep2018UpdateOrLater();
+}
+
+// AddAndConfigureAppContainerProfile deliberately fails if it is called on an
+// unsupported version. This is because for some process types the LPAC is
+// required to provide a sufficiently strong sandbox. Processes where the use of
+// an LPAC is an optional extra should use IsLowPrivilegedAppContainerSupported
+// to check support first.
+static sandbox::ResultCode AddAndConfigureAppContainerProfile(
+ sandbox::TargetPolicy* aPolicy, const nsAString& aPackagePrefix,
+ const nsTArray<sandbox::WellKnownCapabilities>& aWellKnownCapabilites,
+ const nsTArray<const wchar_t*>& aNamedCapabilites) {
+ // CreateAppContainerProfile requires that the profile name is at most 64
+ // characters but 50 on WCOS systems. The size of sha1 is a constant 40,
+ // so validate that the base names are sufficiently short that the total
+ // length is valid on all systems.
+ MOZ_ASSERT(aPackagePrefix.Length() <= 10U,
+ "AppContainer Package prefix too long.");
+
+ if (!IsLowPrivilegedAppContainerSupported()) {
+ return sandbox::SBOX_ERROR_UNSUPPORTED;
+ }
+
+ static nsAutoString uniquePackageStr = []() {
+ // userenv.dll may not have been loaded and some of the chromium sandbox
+ // AppContainer code assumes that it is. Done here to load once.
+ ::LoadLibraryW(L"userenv.dll");
+
+ // Done during the package string initialization so we only do it once.
+ EnsureLpacPermsissionsOnBinDir();
+
+ // This mirrors Edge's use of the exe path for the SHA1 hash to give a
+ // machine unique name per install.
+ nsAutoString ret;
+ char exePathBuf[MAX_PATH];
+ DWORD pathSize = ::GetModuleFileNameA(nullptr, exePathBuf, MAX_PATH);
+ if (!pathSize) {
+ return ret;
+ }
+
+ SHA1Sum sha1Sum;
+ SHA1Sum::Hash sha1Hash;
+ sha1Sum.update(exePathBuf, pathSize);
+ sha1Sum.finish(sha1Hash);
+
+ nsAutoCString hexEncoded;
+ HexEncode(sha1Hash, hexEncoded);
+ ret = NS_ConvertUTF8toUTF16(hexEncoded);
+ return ret;
+ }();
+
+ if (uniquePackageStr.IsEmpty()) {
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE;
+ }
+
+ // The bool parameter is called create_profile, but in fact it tries to create
+ // and then opens if it already exists. So always passing true is fine.
+ bool createOrOpenProfile = true;
+ nsAutoString packageName = aPackagePrefix + uniquePackageStr;
+ sandbox::ResultCode result =
+ aPolicy->AddAppContainerProfile(packageName.get(), createOrOpenProfile);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ // This looks odd, but unfortunately holding a scoped_refptr and
+ // dereferencing has DCHECKs that cause a linking problem.
+ sandbox::AppContainerProfile* profile =
+ aPolicy->GetAppContainerProfile().get();
+ profile->SetEnableLowPrivilegeAppContainer(true);
+
+ for (auto wkCap : aWellKnownCapabilites) {
+ if (!profile->AddCapability(wkCap)) {
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY;
+ }
+ }
+
+ for (auto namedCap : aNamedCapabilites) {
+ if (!profile->AddCapability(namedCap)) {
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY;
+ }
+ }
+
+ return sandbox::SBOX_ALL_OK;
+}
+
+void SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) {
+ MOZ_RELEASE_ASSERT(mPolicy, "mPolicy must be set before this call.");
+
+ sandbox::JobLevel jobLevel;
+ sandbox::TokenLevel accessTokenLevel;
+ sandbox::IntegrityLevel initialIntegrityLevel;
+ sandbox::IntegrityLevel delayedIntegrityLevel;
+
+ // The setting of these levels is pretty arbitrary, but they are a useful (if
+ // crude) tool while we are tightening the policy. Gaps are left to try and
+ // avoid changing their meaning.
+ MOZ_RELEASE_ASSERT(aSandboxLevel >= 1,
+ "Should not be called with aSandboxLevel < 1");
+ if (aSandboxLevel >= 20) {
+ jobLevel = sandbox::JOB_LOCKDOWN;
+ accessTokenLevel = sandbox::USER_LOCKDOWN;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_UNTRUSTED;
+ } else if (aSandboxLevel >= 4) {
+ jobLevel = sandbox::JOB_LOCKDOWN;
+ accessTokenLevel = sandbox::USER_LIMITED;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else if (aSandboxLevel >= 3) {
+ jobLevel = sandbox::JOB_RESTRICTED;
+ accessTokenLevel = sandbox::USER_LIMITED;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else if (aSandboxLevel == 2) {
+ jobLevel = sandbox::JOB_INTERACTIVE;
+ accessTokenLevel = sandbox::USER_INTERACTIVE;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else {
+ MOZ_ASSERT(aSandboxLevel == 1);
+
+ jobLevel = sandbox::JOB_NONE;
+ accessTokenLevel = sandbox::USER_NON_ADMIN;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ }
+
+ // If the process will handle file: URLs, don't allow settings that
+ // block reads.
+ if (aIsFileProcess) {
+ if (accessTokenLevel < sandbox::USER_NON_ADMIN) {
+ accessTokenLevel = sandbox::USER_NON_ADMIN;
+ }
+ if (delayedIntegrityLevel > sandbox::INTEGRITY_LEVEL_LOW) {
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ }
+ }
+
+#if defined(DEBUG)
+ // This is required for a MOZ_ASSERT check in WindowsMessageLoop.cpp
+ // WinEventHook, see bug 1366694 for details.
+ DWORD uiExceptions = JOB_OBJECT_UILIMIT_HANDLES;
+#else
+ DWORD uiExceptions = 0;
+#endif
+ sandbox::ResultCode result = SetJobLevel(mPolicy, jobLevel, uiExceptions);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Setting job level failed, have you set memory limit when "
+ "jobLevel == JOB_NONE?");
+
+ // If the delayed access token is not restricted we don't want the initial one
+ // to be either, because it can interfere with running from a network drive.
+ sandbox::TokenLevel initialAccessTokenLevel =
+ (accessTokenLevel == sandbox::USER_UNPROTECTED ||
+ accessTokenLevel == sandbox::USER_NON_ADMIN)
+ ? sandbox::USER_UNPROTECTED
+ : sandbox::USER_RESTRICTED_SAME_ACCESS;
+
+ result = mPolicy->SetTokenLevel(initialAccessTokenLevel, accessTokenLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Lockdown level cannot be USER_UNPROTECTED or USER_LAST "
+ "if initial level was USER_RESTRICTED_SAME_ACCESS");
+
+ result = mPolicy->SetIntegrityLevel(initialIntegrityLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "SetIntegrityLevel should never fail, what happened?");
+ result = mPolicy->SetDelayedIntegrityLevel(delayedIntegrityLevel);
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "SetDelayedIntegrityLevel should never fail, what happened?");
+
+ if (aSandboxLevel > 5) {
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+ }
+
+ if (aSandboxLevel > 4) {
+ // Alternate winstation breaks native theming.
+ bool useAlternateWinstation =
+ StaticPrefs::widget_non_native_theme_enabled();
+ result = mPolicy->SetAlternateDesktop(useAlternateWinstation);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+ }
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+ sandbox::MITIGATION_DEP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+
+#if defined(_M_ARM64)
+ // Disable CFG on older versions of ARM64 Windows to avoid a crash in COM.
+ if (!IsWin10Sep2018UpdateOrLater()) {
+ mitigations |= sandbox::MITIGATION_CONTROL_FLOW_GUARD_DISABLE;
+ }
+#endif
+
+ if (aSandboxLevel > 3) {
+ // If we're running from a network drive then we can't block loading from
+ // remote locations. Strangely using MITIGATION_IMAGE_LOAD_NO_LOW_LABEL in
+ // this situation also means the process fails to start (bug 1423296).
+ if (!sRunningFromNetworkDrive) {
+ mitigations |= sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE |
+ sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL;
+ }
+ }
+
+ if (StaticPrefs::security_sandbox_content_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetProcessMitigations.");
+
+ nsIXULRuntime::ContentWin32kLockdownState win32kLockdownState =
+ GetContentWin32kLockdownState();
+
+ LOG_W("Win32k Lockdown State: '%s'",
+ ContentWin32kLockdownStateToString(win32kLockdownState));
+
+ if (GetContentWin32kLockdownEnabled()) {
+ result = AddWin32kLockdownPolicy(mPolicy, false);
+ MOZ_RELEASE_ASSERT(result == sandbox::SBOX_ALL_OK,
+ "Failed to add the win32k lockdown policy");
+ }
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // We still have edge cases where the child at low integrity can't read some
+ // files, so add a rule to allow read access to everything when required.
+ if (aSandboxLevel == 1 || aIsFileProcess) {
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY, L"*");
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, "
+ "what happened?");
+ } else {
+ // Add rule to allow access to user specific fonts.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sLocalAppDataDir, u"\\Microsoft\\Windows\\Fonts\\*"_ns);
+
+ // Add rule to allow read access to installation directory.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sBinDir, u"\\*"_ns);
+
+ // Add rule to allow read access to the chrome directory within profile.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sProfileDir, u"\\chrome\\*"_ns);
+
+ // Add rule to allow read access to the extensions directory within profile.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sProfileDir, u"\\extensions\\*"_ns);
+
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ // Add rule to allow read access to the per-user extensions directory.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sUserExtensionsDir, u"\\*"_ns);
+#endif
+ }
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The content process needs to be able to duplicate named pipes back to the
+ // broker and other child processes, which are File type handles.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"File");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"File");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The content process needs to be able to duplicate shared memory handles,
+ // which are Section handles, to the broker process and other child processes.
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The content process needs to be able to duplicate semaphore handles,
+ // to the broker process and other child processes.
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Semaphore");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Semaphore");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Allow content processes to use complex line breaking brokering.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_LINE_BREAK,
+ sandbox::TargetPolicy::LINE_BREAK_ALLOW, nullptr);
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ if (aSandboxLevel >= 20) {
+ // Content process still needs to be able to read fonts.
+ wchar_t* fontsPath;
+ if (SUCCEEDED(
+ ::SHGetKnownFolderPath(FOLDERID_Fonts, 0, nullptr, &fontsPath))) {
+ std::wstring fontsStr = fontsPath;
+ ::CoTaskMemFree(fontsPath);
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ fontsStr.c_str());
+ if (sandbox::SBOX_ALL_OK != result) {
+ NS_ERROR("Failed to add fonts dir read access policy rule.");
+ LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
+ fontsStr.c_str());
+ }
+
+ fontsStr += L"\\*";
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ fontsStr.c_str());
+ if (sandbox::SBOX_ALL_OK != result) {
+ NS_ERROR("Failed to add fonts read access policy rule.");
+ LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
+ fontsStr.c_str());
+ }
+ }
+
+ // We still currently create IPC named pipes in the content process.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
+ sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\chrome.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail.");
+ }
+}
+
+void SandboxBroker::SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) {
+ MOZ_RELEASE_ASSERT(mPolicy, "mPolicy must be set before this call.");
+
+ sandbox::JobLevel jobLevel;
+ sandbox::TokenLevel accessTokenLevel;
+ sandbox::IntegrityLevel initialIntegrityLevel;
+ sandbox::IntegrityLevel delayedIntegrityLevel;
+
+ // The setting of these levels is pretty arbitrary, but they are a useful (if
+ // crude) tool while we are tightening the policy. Gaps are left to try and
+ // avoid changing their meaning.
+ if (aSandboxLevel >= 2) {
+ jobLevel = sandbox::JOB_NONE;
+ accessTokenLevel = sandbox::USER_LIMITED;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else {
+ MOZ_RELEASE_ASSERT(aSandboxLevel >= 1,
+ "Should not be called with aSandboxLevel < 1");
+ jobLevel = sandbox::JOB_NONE;
+ accessTokenLevel = sandbox::USER_NON_ADMIN;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ }
+
+ sandbox::ResultCode result =
+ SetJobLevel(mPolicy, 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?");
+
+ // If the delayed access token is not restricted we don't want the initial one
+ // to be either, because it can interfere with running from a network drive.
+ sandbox::TokenLevel initialAccessTokenLevel =
+ (accessTokenLevel == sandbox::USER_UNPROTECTED ||
+ accessTokenLevel == sandbox::USER_NON_ADMIN)
+ ? sandbox::USER_UNPROTECTED
+ : sandbox::USER_RESTRICTED_SAME_ACCESS;
+
+ result = mPolicy->SetTokenLevel(initialAccessTokenLevel, accessTokenLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Lockdown level cannot be USER_UNPROTECTED or USER_LAST "
+ "if initial level was USER_RESTRICTED_SAME_ACCESS");
+
+ result = mPolicy->SetIntegrityLevel(initialIntegrityLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "SetIntegrityLevel should never fail, what happened?");
+ result = mPolicy->SetDelayedIntegrityLevel(delayedIntegrityLevel);
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "SetDelayedIntegrityLevel should never fail, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+ sandbox::MITIGATION_DEP;
+
+ if (StaticPrefs::security_sandbox_gpu_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetProcessMitigations.");
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The GPU process needs to write to a shader cache for performance reasons
+ if (sProfileDir) {
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_DIR_ANY,
+ sProfileDir, u"\\shader-cache"_ns);
+
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ sProfileDir, u"\\shader-cache\\*"_ns);
+ }
+
+ // The process needs to be able to duplicate shared memory handles,
+ // which are Section handles, to the broker process and other child processes.
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+}
+
+#define SANDBOX_ENSURE_SUCCESS(result, message) \
+ do { \
+ MOZ_ASSERT(sandbox::SBOX_ALL_OK == result, message); \
+ if (sandbox::SBOX_ALL_OK != result) return false; \
+ } while (0)
+
+bool SandboxBroker::SetSecurityLevelForRDDProcess() {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ SetJobLevel(mPolicy, sandbox::JOB_LOCKDOWN, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_LIMITED);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetAlternateDesktop(true);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+
+ result = mPolicy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+
+ result = mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetDelayedIntegrityLevel should never fail with "
+ "these arguments, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+
+ if (StaticPrefs::security_sandbox_rdd_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ if (StaticPrefs::security_sandbox_rdd_acg_enabled()) {
+ // The RDD process depends on msmpeg2vdec.dll.
+ mitigations |= DynamicCodeFlagForSystemMediaLibraries();
+ }
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ result = AddCigToPolicy(mPolicy);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to initialize signed policy rules.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The process needs to be able to duplicate shared memory handles,
+ // which are Section handles, to the content processes.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // This section is needed to avoid an assert during crash reporting code
+ // when running mochitests. The assertion is here:
+ // toolkit/crashreporter/nsExceptionHandler.cpp:2041
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+
+bool SandboxBroker::SetSecurityLevelForSocketProcess() {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ SetJobLevel(mPolicy, sandbox::JOB_LOCKDOWN, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_LIMITED);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetAlternateDesktop(true);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+
+ result = mPolicy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+
+ result =
+ mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetDelayedIntegrityLevel should never fail with "
+ "these arguments, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+
+ if (StaticPrefs::security_sandbox_socket_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ if (StaticPrefs::security_sandbox_socket_win32k_disable()) {
+ result = AddWin32kLockdownPolicy(mPolicy, false);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to add the win32k lockdown policy");
+ }
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER |
+ sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ result = AddCigToPolicy(mPolicy);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to initialize signed policy rules.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // This section is needed to avoid an assert during crash reporting code
+ // when running mochitests. The assertion is here:
+ // toolkit/crashreporter/nsExceptionHandler.cpp:2041
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+
+// A strict base sandbox for utility sandboxes to adapt.
+struct UtilitySandboxProps {
+ sandbox::JobLevel mJobLevel = sandbox::JOB_LOCKDOWN;
+
+ sandbox::TokenLevel mInitialTokenLevel = sandbox::USER_RESTRICTED_SAME_ACCESS;
+ sandbox::TokenLevel mDelayedTokenLevel = sandbox::USER_LOCKDOWN;
+
+ sandbox::IntegrityLevel mInitialIntegrityLevel = // before lockdown
+ sandbox::INTEGRITY_LEVEL_LOW;
+ sandbox::IntegrityLevel mDelayedIntegrityLevel = // after lockdown
+ sandbox::INTEGRITY_LEVEL_UNTRUSTED;
+
+ bool mUseAlternateWindowStation = true;
+ bool mUseAlternateDesktop = true;
+ bool mLockdownDefaultDacl = true;
+ bool mAddRestrictingRandomSid = true;
+ bool mUseWin32kLockdown = true;
+ bool mUseCig = true;
+
+ sandbox::MitigationFlags mInitialMitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32 |
+ sandbox::MITIGATION_CET_COMPAT_MODE;
+
+ sandbox::MitigationFlags mDelayedMitigations =
+ sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER |
+ sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+
+ // Low Privileged Application Container settings;
+ nsString mPackagePrefix;
+ nsTArray<sandbox::WellKnownCapabilities> mWellKnownCapabilites;
+ nsTArray<const wchar_t*> mNamedCapabilites;
+};
+
+struct GenericUtilitySandboxProps : public UtilitySandboxProps {};
+
+struct UtilityAudioDecodingWmfSandboxProps : public UtilitySandboxProps {
+ UtilityAudioDecodingWmfSandboxProps() {
+ mDelayedTokenLevel = sandbox::USER_LIMITED;
+ mDelayedMitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+#ifdef MOZ_WMF
+ if (StaticPrefs::security_sandbox_utility_wmf_acg_enabled()) {
+ mDelayedMitigations |= DynamicCodeFlagForSystemMediaLibraries();
+ }
+#else
+ mDelayedMitigations |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+#endif // MOZ_WMF
+ }
+};
+
+#ifdef MOZ_WMF_MEDIA_ENGINE
+struct UtilityMfMediaEngineCdmSandboxProps : public UtilitySandboxProps {
+ UtilityMfMediaEngineCdmSandboxProps() {
+ mJobLevel = sandbox::JOB_INTERACTIVE;
+ mInitialTokenLevel = sandbox::USER_UNPROTECTED;
+ mDelayedTokenLevel = sandbox::USER_UNPROTECTED;
+ mUseAlternateDesktop = false;
+ mUseAlternateWindowStation = false;
+ mLockdownDefaultDacl = false;
+ mAddRestrictingRandomSid = false;
+
+ // When we have an LPAC we can't set an integrity level and the process will
+ // default to low integrity anyway. Without an LPAC using low integrity
+ // causes problems with the CDMs.
+ mInitialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LAST;
+ mDelayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LAST;
+
+ if (StaticPrefs::security_sandbox_utility_wmf_cdm_lpac_enabled()) {
+ mPackagePrefix = u"fx.sb.cdm"_ns;
+ mWellKnownCapabilites = {
+ sandbox::WellKnownCapabilities::kPrivateNetworkClientServer,
+ sandbox::WellKnownCapabilities::kInternetClient,
+ };
+ mNamedCapabilites = {
+ L"lpacCom",
+ L"lpacIdentityServices",
+ L"lpacMedia",
+ L"lpacPnPNotifications",
+ L"lpacServicesManagement",
+ L"lpacSessionManagement",
+ L"lpacAppExperience",
+ L"lpacInstrumentation",
+ L"lpacCryptoServices",
+ L"lpacEnterprisePolicyChangeNotifications",
+ L"mediaFoundationCdmFiles",
+ L"lpacMediaFoundationCdmData",
+ L"registryRead",
+ kLpacFirefoxInstallFiles,
+ L"lpacDeviceAccess",
+ };
+ }
+ mUseWin32kLockdown = false;
+ mInitialMitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR |
+ sandbox::MITIGATION_HEAP_TERMINATE | sandbox::MITIGATION_SEHOP |
+ sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_CET_COMPAT_MODE;
+
+ mDelayedMitigations = sandbox::MITIGATION_DLL_SEARCH_ORDER;
+ }
+};
+#endif
+
+struct WindowsUtilitySandboxProps : public UtilitySandboxProps {
+ WindowsUtilitySandboxProps() {
+ mJobLevel = sandbox::JOB_INTERACTIVE;
+ mDelayedTokenLevel = sandbox::USER_RESTRICTED_SAME_ACCESS;
+ mUseAlternateWindowStation = false;
+ mInitialIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
+ mDelayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
+ mUseWin32kLockdown = false;
+ mUseCig = false;
+ mDelayedMitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+ }
+};
+
+static const char* WellKnownCapabilityNames[] = {
+ "InternetClient",
+ "InternetClientServer",
+ "PrivateNetworkClientServer",
+ "PicturesLibrary",
+ "VideosLibrary",
+ "MusicLibrary",
+ "DocumentsLibrary",
+ "EnterpriseAuthentication",
+ "SharedUserCertificates",
+ "RemovableStorage",
+ "Appointments",
+ "Contacts",
+};
+
+void LogUtilitySandboxProps(const UtilitySandboxProps& us) {
+ if (!static_cast<LogModule*>(sSandboxBrokerLog)->ShouldLog(LogLevel::Debug)) {
+ return;
+ }
+
+ nsAutoCString logMsg;
+ logMsg.AppendPrintf("Building sandbox for utility process:\n");
+ logMsg.AppendPrintf("\tJob Level: %d\n", static_cast<int>(us.mJobLevel));
+ logMsg.AppendPrintf("\tInitial Token Level: %d\n",
+ static_cast<int>(us.mInitialTokenLevel));
+ logMsg.AppendPrintf("\tDelayed Token Level: %d\n",
+ static_cast<int>(us.mDelayedTokenLevel));
+ logMsg.AppendPrintf("\tInitial Integrity Level: %d\n",
+ static_cast<int>(us.mInitialIntegrityLevel));
+ logMsg.AppendPrintf("\tDelayed Integrity Level: %d\n",
+ static_cast<int>(us.mDelayedIntegrityLevel));
+ logMsg.AppendPrintf("\tUse Alternate Window Station: %s\n",
+ us.mUseAlternateWindowStation ? "yes" : "no");
+ logMsg.AppendPrintf("\tUse Alternate Desktop: %s\n",
+ us.mUseAlternateDesktop ? "yes" : "no");
+ logMsg.AppendPrintf("\tLockdown Default Dacl: %s\n",
+ us.mLockdownDefaultDacl ? "yes" : "no");
+ logMsg.AppendPrintf("\tAdd Random Restricting SID: %s\n",
+ us.mAddRestrictingRandomSid ? "yes" : "no");
+ logMsg.AppendPrintf("\tUse Win32k Lockdown: %s\n",
+ us.mUseWin32kLockdown ? "yes" : "no");
+ logMsg.AppendPrintf("\tUse CIG: %s\n", us.mUseCig ? "yes" : "no");
+ logMsg.AppendPrintf("\tInitial mitigations: %016llx\n",
+ static_cast<uint64_t>(us.mInitialMitigations));
+ logMsg.AppendPrintf("\tDelayed mitigations: %016llx\n",
+ static_cast<uint64_t>(us.mDelayedMitigations));
+ if (us.mPackagePrefix.IsEmpty()) {
+ logMsg.AppendPrintf("\tNo Low Privileged Application Container\n");
+ } else {
+ logMsg.AppendPrintf("\tLow Privileged Application Container Settings:\n");
+ logMsg.AppendPrintf("\t\tPackage Name Prefix: %S\n",
+ static_cast<wchar_t*>(us.mPackagePrefix.get()));
+ logMsg.AppendPrintf("\t\tWell Known Capabilities:\n");
+ for (auto wkCap : us.mWellKnownCapabilites) {
+ logMsg.AppendPrintf("\t\t\t%s\n", WellKnownCapabilityNames[wkCap]);
+ }
+ logMsg.AppendPrintf("\t\tNamed Capabilities:\n");
+ for (auto namedCap : us.mNamedCapabilites) {
+ logMsg.AppendPrintf("\t\t\t%S\n", namedCap);
+ }
+ }
+
+ LOG_D("%s", logMsg.get());
+}
+
+bool BuildUtilitySandbox(sandbox::TargetPolicy* policy,
+ const UtilitySandboxProps& us) {
+ LogUtilitySandboxProps(us);
+
+ auto result = SetJobLevel(policy, us.mJobLevel, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+
+ result = policy->SetTokenLevel(us.mInitialTokenLevel, us.mDelayedTokenLevel);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ if (us.mInitialIntegrityLevel != sandbox::INTEGRITY_LEVEL_LAST) {
+ result = policy->SetIntegrityLevel(us.mInitialIntegrityLevel);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+ }
+
+ if (us.mDelayedIntegrityLevel != sandbox::INTEGRITY_LEVEL_LAST) {
+ result = policy->SetDelayedIntegrityLevel(us.mDelayedIntegrityLevel);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+ }
+
+ if (us.mUseAlternateDesktop) {
+ result = policy->SetAlternateDesktop(us.mUseAlternateWindowStation);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+ }
+
+ if (us.mLockdownDefaultDacl) {
+ policy->SetLockdownDefaultDacl();
+ }
+ if (us.mAddRestrictingRandomSid) {
+ policy->AddRestrictingRandomSid();
+ }
+
+ result = policy->SetProcessMitigations(us.mInitialMitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ result = policy->SetDelayedProcessMitigations(us.mDelayedMitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // Win32k lockdown might not work on earlier versions
+ // Bug 1719212, 1769992
+ if (us.mUseWin32kLockdown && IsWin10FallCreatorsUpdateOrLater()) {
+ result = AddWin32kLockdownPolicy(policy, false);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to add the win32k lockdown policy");
+ }
+
+ if (us.mUseCig) {
+ bool alwaysProxyBinDirLoading = mozilla::HasPackageIdentity();
+ result = AddCigToPolicy(policy, alwaysProxyBinDirLoading);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to initialize signed policy rules.");
+ }
+
+ if (!us.mPackagePrefix.IsEmpty()) {
+ MOZ_ASSERT(us.mInitialIntegrityLevel == sandbox::INTEGRITY_LEVEL_LAST,
+ "Initial integrity level cannot be specified if using an LPAC.");
+
+ result = AddAndConfigureAppContainerProfile(policy, us.mPackagePrefix,
+ us.mWellKnownCapabilites,
+ us.mNamedCapabilites);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to configure AppContainer profile.");
+ }
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+
+bool SandboxBroker::SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) {
+ if (!mPolicy) {
+ return false;
+ }
+
+ switch (aSandbox) {
+ case mozilla::ipc::SandboxingKind::GENERIC_UTILITY:
+ return BuildUtilitySandbox(mPolicy, GenericUtilitySandboxProps());
+ case mozilla::ipc::SandboxingKind::UTILITY_AUDIO_DECODING_WMF:
+ return BuildUtilitySandbox(mPolicy,
+ UtilityAudioDecodingWmfSandboxProps());
+#ifdef MOZ_WMF_MEDIA_ENGINE
+ case mozilla::ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM:
+ return BuildUtilitySandbox(mPolicy,
+ UtilityMfMediaEngineCdmSandboxProps());
+#endif
+ case mozilla::ipc::SandboxingKind::WINDOWS_UTILS:
+ return BuildUtilitySandbox(mPolicy, WindowsUtilitySandboxProps());
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown sandboxing value");
+ return false;
+ }
+}
+
+bool SandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch) {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ SetJobLevel(mPolicy, sandbox::JOB_LOCKDOWN, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+ auto level = (aLevel == Restricted) ? sandbox::USER_RESTRICTED
+ : sandbox::USER_LOCKDOWN;
+ result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, level);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetAlternateDesktop(true);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+
+ result = mPolicy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ MOZ_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "SetIntegrityLevel should never fail with these arguments, what "
+ "happened?");
+
+ result =
+ mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP;
+
+ if (StaticPrefs::security_sandbox_gmp_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ // Chromium only implements win32k disable for PPAPI on Win10 or later,
+ // believed to be due to the interceptions required for OPM.
+ if (StaticPrefs::security_sandbox_gmp_win32k_disable() && IsWin10OrLater()) {
+ result = AddWin32kLockdownPolicy(mPolicy, true);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to add the win32k lockdown policy");
+ }
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+#ifdef DEBUG
+ // The plugin process can't create named events, but we'll
+ // make an exception for the events used in logging. Removing
+ // this will break EME in debug builds.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SYNC,
+ sandbox::TargetPolicy::EVENTS_ALLOW_ANY,
+ L"ChromeIPCLog.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+#endif
+
+ // The following rules were added because, during analysis of an EME
+ // plugin during development, these registry keys were accessed when
+ // loading the plugin. Commenting out these policy exceptions caused
+ // plugin loading to fail, so they are necessary for proper functioning
+ // of at least one EME plugin.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Control Panel\\Desktop");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Control Panel\\Desktop\\LanguageConfiguration");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_"
+ L"MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SideBySide");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The following rules were added because, during analysis of an EME
+ // plugin during development, these registry keys were accessed when
+ // loading the plugin. Commenting out these policy exceptions did not
+ // cause anything to break during initial testing, but might cause
+ // unforeseen issues down the road.
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\MUI\\Settings");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Control "
+ L"Panel\\Desktop");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Control Panel\\Desktop\\PreferredUILanguages");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_"
+ L"MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVer"
+ L"sion\\SideBySide\\PreferExternalManifest");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The following rules were added to allow a GMP to be loaded when any
+ // AppLocker DLL rules are specified. If the rules specifically block the DLL
+ // then it will not load.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ L"\\Device\\SrpDevice");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Srp\\GP\\");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+ // On certain Windows versions there is a double slash before GP in the path.
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Srp\\\\GP\\");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The GMP process needs to be able to share memory with the main process for
+ // crash reporting. On arm64 when we are launching remotely via an x86 broker,
+ // we need the rule to be HANDLES_DUP_ANY, because we still need to duplicate
+ // to the main process not the child's broker.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ aIsRemoteLaunch
+ ? sandbox::TargetPolicy::HANDLES_DUP_ANY
+ : sandbox::TargetPolicy::HANDLES_DUP_BROKER,
+ L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+#undef SANDBOX_ENSURE_SUCCESS
+
+bool SandboxBroker::AllowReadFile(wchar_t const* file) {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY, file);
+ if (sandbox::SBOX_ALL_OK != result) {
+ LOG_E("Failed (ResultCode %d) to add read access to: %S", result, file);
+ return false;
+ }
+
+ return true;
+}
+
+/* static */
+bool SandboxBroker::AddTargetPeer(HANDLE aPeerProcess) {
+ if (!sBrokerService) {
+ return false;
+ }
+
+ sandbox::ResultCode result = sBrokerService->AddTargetPeer(aPeerProcess);
+ return (sandbox::SBOX_ALL_OK == result);
+}
+
+void SandboxBroker::AddHandleToShare(HANDLE aHandle) {
+ mPolicy->AddHandleToShare(aHandle);
+}
+
+bool SandboxBroker::IsWin32kLockedDown() {
+ return mPolicy->GetProcessMitigations() & sandbox::MITIGATION_WIN32K_DISABLE;
+}
+
+void SandboxBroker::ApplyLoggingPolicy() {
+ MOZ_ASSERT(mPolicy);
+
+ // Add dummy rules, so that we can log in the interception code.
+ // We already have a file interception set up for the client side of pipes.
+ // Also, passing just "dummy" for file system policy causes win_utils.cc
+ // IsReparsePoint() to loop.
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
+ sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, L"dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_PROCESS,
+ sandbox::TargetPolicy::PROCESS_MIN_EXEC, L"dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SYNC,
+ sandbox::TargetPolicy::EVENTS_ALLOW_READONLY, L"dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"dummy");
+}
+
+SandboxBroker::~SandboxBroker() {
+ if (mPolicy) {
+ mPolicy->Release();
+ mPolicy = nullptr;
+ }
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
new file mode 100644
index 0000000000..752fbaa9aa
--- /dev/null
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; 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 __SECURITY_SANDBOX_SANDBOXBROKER_H__
+#define __SECURITY_SANDBOX_SANDBOXBROKER_H__
+
+#include <stdint.h>
+#include <windows.h>
+
+#include "build/build_config.h"
+#include "mozilla/ipc/EnvironmentMap.h"
+#include "nsCOMPtr.h"
+#include "nsXULAppAPI.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+#include "mozilla/ipc/LaunchError.h"
+#include "mozilla/Result.h"
+
+namespace sandbox {
+class BrokerServices;
+class TargetPolicy;
+} // namespace sandbox
+
+namespace mozilla {
+
+class AbstractSandboxBroker {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractSandboxBroker)
+
+ virtual void Shutdown() = 0;
+ virtual Result<Ok, mozilla::ipc::LaunchError> LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA* aCachedNtdllThunk,
+ void** aProcessHandle) = 0;
+
+ // Security levels for different types of processes
+ virtual void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) = 0;
+
+ virtual void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) = 0;
+ virtual bool SetSecurityLevelForRDDProcess() = 0;
+ virtual bool SetSecurityLevelForSocketProcess() = 0;
+ virtual bool SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) = 0;
+
+ enum SandboxLevel { LockDown, Restricted };
+ virtual bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch = false) = 0;
+
+ // File system permissions
+ virtual bool AllowReadFile(wchar_t const* file) = 0;
+
+ /**
+ * Share a HANDLE with the child process. The HANDLE will be made available
+ * in the child process at the memory address
+ * |reinterpret_cast<uintptr_t>(aHandle)|. It is the caller's responsibility
+ * to communicate this address to the child.
+ */
+ virtual void AddHandleToShare(HANDLE aHandle) = 0;
+
+ /**
+ * @return true if policy has win32k locked down, otherwise false
+ */
+ virtual bool IsWin32kLockedDown() = 0;
+
+ protected:
+ virtual ~AbstractSandboxBroker() {}
+};
+
+class SandboxBroker : public AbstractSandboxBroker {
+ public:
+ SandboxBroker();
+
+ static void Initialize(sandbox::BrokerServices* aBrokerServices);
+
+ void Shutdown() override {}
+
+ /**
+ * Do initialization that depends on parts of the Gecko machinery having been
+ * created first.
+ */
+ static void GeckoDependentInitialize();
+
+ Result<Ok, mozilla::ipc::LaunchError> LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA* aCachedNtdllThunk,
+ void** aProcessHandle) override;
+ virtual ~SandboxBroker();
+
+ // Security levels for different types of processes
+ void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) override;
+
+ void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) override;
+ bool SetSecurityLevelForRDDProcess() override;
+ bool SetSecurityLevelForSocketProcess() override;
+ bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch = false) override;
+ bool SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) override;
+
+ // File system permissions
+ bool AllowReadFile(wchar_t const* file) override;
+
+ /**
+ * Exposes AddTargetPeer from broker services, so that non-sandboxed
+ * processes can be added as handle duplication targets.
+ */
+ static bool AddTargetPeer(HANDLE aPeerProcess);
+
+ /**
+ * Share a HANDLE with the child process. The HANDLE will be made available
+ * in the child process at the memory address
+ * |reinterpret_cast<uintptr_t>(aHandle)|. It is the caller's responsibility
+ * to communicate this address to the child.
+ */
+ void AddHandleToShare(HANDLE aHandle) override;
+
+ bool IsWin32kLockedDown() final;
+
+ // Set up dummy interceptions via the broker, so we can log calls.
+ void ApplyLoggingPolicy();
+
+ private:
+ static bool sRunningFromNetworkDrive;
+ sandbox::TargetPolicy* mPolicy;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/security/sandbox/win/src/sandboxtarget/moz.build b/security/sandbox/win/src/sandboxtarget/moz.build
new file mode 100644
index 0000000000..3c28eebba2
--- /dev/null
+++ b/security/sandbox/win/src/sandboxtarget/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ "sandboxTarget.h",
+]
+
+SOURCES += [
+ "sandboxTarget.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+ "/security/sandbox/chromium-shim",
+]
+
+DEFINES["UNICODE"] = True
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp b/security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp
new file mode 100644
index 0000000000..b0b0876bca
--- /dev/null
+++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#include "sandboxTarget.h"
+
+#include "sandbox/win/src/sandbox.h"
+
+namespace mozilla {
+
+// We need to define this function out of line so that clang-cl doesn't inline
+// it.
+/* static */
+SandboxTarget* SandboxTarget::Instance() {
+ static SandboxTarget sb;
+ return &sb;
+}
+
+void SandboxTarget::StartSandbox() {
+ if (mTargetServices) {
+ mTargetServices->LowerToken();
+ NotifyStartObservers();
+ }
+}
+
+void SandboxTarget::NotifyStartObservers() {
+ for (auto&& obs : mStartObservers) {
+ if (!obs) {
+ continue;
+ }
+
+ obs();
+ }
+
+ mStartObservers.clear();
+}
+
+bool SandboxTarget::BrokerDuplicateHandle(HANDLE aSourceHandle,
+ DWORD aTargetProcessId,
+ HANDLE* aTargetHandle,
+ DWORD aDesiredAccess,
+ DWORD aOptions) {
+ if (!mTargetServices) {
+ return false;
+ }
+
+ sandbox::ResultCode result = mTargetServices->DuplicateHandle(
+ aSourceHandle, aTargetProcessId, aTargetHandle, aDesiredAccess, aOptions);
+ return (sandbox::SBOX_ALL_OK == result);
+}
+bool SandboxTarget::GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before) {
+ if (!mTargetServices) {
+ return false;
+ }
+
+ sandbox::ResultCode result =
+ mTargetServices->GetComplexLineBreaks(text, length, break_before);
+ return (sandbox::SBOX_ALL_OK == result);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/sandboxtarget/sandboxTarget.h b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h
new file mode 100644
index 0000000000..7867f0bb33
--- /dev/null
+++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 __SECURITY_SANDBOX_SANDBOXTARGET_H__
+#define __SECURITY_SANDBOX_SANDBOXTARGET_H__
+
+#include <windows.h>
+
+#include <functional>
+#include <list>
+#include <utility>
+
+#include "mozilla/Assertions.h"
+
+namespace sandbox {
+class TargetServices;
+}
+
+namespace mozilla {
+
+class SandboxTarget {
+ public:
+ /**
+ * Obtains a pointer to the singleton instance
+ */
+ static SandboxTarget* Instance();
+
+ /**
+ * 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 used
+ */
+ void SetTargetServices(sandbox::TargetServices* aTargetServices) {
+ MOZ_ASSERT(aTargetServices);
+ MOZ_ASSERT(!mTargetServices,
+ "Sandbox TargetServices must only be set once.");
+
+ mTargetServices = aTargetServices;
+ }
+
+ template <typename CallbackT>
+ void RegisterSandboxStartCallback(CallbackT&& aCallback) {
+ mStartObservers.push_back(std::forward<CallbackT>(aCallback));
+ }
+
+ /**
+ * Called by the library that wants to "start" the sandbox, i.e. change to the
+ * more secure delayed / lockdown policy.
+ */
+ void StartSandbox();
+
+ /**
+ * Used to duplicate handles via the broker process. The permission for the
+ * handle type and target process has to have been set on the sandbox policy.
+ */
+ bool BrokerDuplicateHandle(HANDLE aSourceHandle, DWORD aTargetProcessId,
+ HANDLE* aTargetHandle, DWORD aDesiredAccess,
+ DWORD aOptions);
+
+ bool GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before);
+
+ protected:
+ SandboxTarget() : mTargetServices(nullptr) {}
+
+ sandbox::TargetServices* mTargetServices;
+
+ private:
+ void NotifyStartObservers();
+ std::list<std::function<void()>> mStartObservers;
+};
+
+} // namespace mozilla
+
+#endif