diff options
Diffstat (limited to 'security/sandbox/win')
-rw-r--r-- | security/sandbox/win/SandboxInitialization.cpp | 81 | ||||
-rw-r--r-- | security/sandbox/win/SandboxInitialization.h | 47 | ||||
-rw-r--r-- | security/sandbox/win/src/sandboxbroker/moz.build | 33 | ||||
-rw-r--r-- | security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp | 595 | ||||
-rw-r--r-- | security/sandbox/win/src/sandboxbroker/sandboxBroker.h | 63 | ||||
-rw-r--r-- | security/sandbox/win/src/sandboxtarget/moz.build | 20 | ||||
-rw-r--r-- | security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp | 21 | ||||
-rw-r--r-- | security/sandbox/win/src/sandboxtarget/sandboxTarget.h | 83 | ||||
-rw-r--r-- | security/sandbox/win/wow_helper/Makefile.in | 47 | ||||
-rw-r--r-- | security/sandbox/win/wow_helper/moz.build | 30 |
10 files changed, 1020 insertions, 0 deletions
diff --git a/security/sandbox/win/SandboxInitialization.cpp b/security/sandbox/win/SandboxInitialization.cpp new file mode 100644 index 000000000..e587c2598 --- /dev/null +++ b/security/sandbox/win/SandboxInitialization.cpp @@ -0,0 +1,81 @@ +/* -*- 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 "sandbox/win/src/sandbox_factory.h" + +namespace mozilla { +namespace sandboxing { + +static sandbox::TargetServices* +InitializeTargetServices() +{ + 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() +{ + 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. + sandbox::TargetPolicy* policy = brokerServices->CreatePolicy(); + sandbox::ResultCode result = policy->CreateAlternateDesktop(true); + policy->Release(); + + return brokerServices; +} + +sandbox::BrokerServices* +GetInitializedBrokerServices() +{ + static sandbox::BrokerServices* sInitializedBrokerServices = + InitializeBrokerServices(); + + return sInitializedBrokerServices; +} + +} // sandboxing +} // mozilla diff --git a/security/sandbox/win/SandboxInitialization.h b/security/sandbox/win/SandboxInitialization.h new file mode 100644 index 000000000..e5be08904 --- /dev/null +++ b/security/sandbox/win/SandboxInitialization.h @@ -0,0 +1,47 @@ +/* -*- 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; +} + +// 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(); + +} // sandboxing +} // mozilla + +#endif // mozilla_sandboxing_SandboxInitialization_h diff --git a/security/sandbox/win/src/sandboxbroker/moz.build b/security/sandbox/win/src/sandboxbroker/moz.build new file mode 100644 index 000000000..4a9d01cc5 --- /dev/null +++ b/security/sandbox/win/src/sandboxbroker/moz.build @@ -0,0 +1,33 @@ +# -*- 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', 'SANDBOX_EXPORTS'): + DEFINES[var] = True + +LOCAL_INCLUDES += [ + '/security/sandbox/chromium', +] + +DISABLE_STL_WRAPPING = True + +OS_LIBS += [ + 'dbghelp', +] + +if CONFIG['_MSC_VER']: + CXXFLAGS += [ + '-wd4275', # non dll-interface class exception used as base for dll-interface class + ] + +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 000000000..10b796268 --- /dev/null +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -0,0 +1,595 @@ +/* -*- 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/. */ + +#include "sandboxBroker.h" + +#include "base/win/windows_version.h" +#include "mozilla/Assertions.h" +#include "mozilla/Logging.h" +#include "sandbox/win/src/sandbox.h" +#include "sandbox/win/src/security_level.h" + +namespace mozilla +{ + +sandbox::BrokerServices *SandboxBroker::sBrokerService = nullptr; + +static LazyLogModule sSandboxBrokerLog("SandboxBroker"); + +#define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__)) + +/* static */ +void +SandboxBroker::Initialize(sandbox::BrokerServices* aBrokerServices) +{ + sBrokerService = aBrokerServices; +} + +SandboxBroker::SandboxBroker() +{ + if (sBrokerService) { + mPolicy = sBrokerService->CreatePolicy(); + } else { + mPolicy = nullptr; + } +} + +bool +SandboxBroker::LaunchApp(const wchar_t *aPath, + const wchar_t *aArguments, + const bool aEnableLogging, + void **aProcessHandle) +{ + if (!sBrokerService || !mPolicy) { + return false; + } + + // 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 + + // Ceate the sandboxed process + PROCESS_INFORMATION targetInfo = {0}; + sandbox::ResultCode result; + result = sBrokerService->SpawnTarget(aPath, aArguments, mPolicy, &targetInfo); + if (sandbox::SBOX_ALL_OK != result) { + return false; + } + + // 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 true; +} + +#if defined(MOZ_CONTENT_SANDBOX) +void +SandboxBroker::SetSecurityLevelForContentProcess(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. + 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 >= 10) { + 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 if (aSandboxLevel == 1) { + jobLevel = sandbox::JOB_NONE; + accessTokenLevel = sandbox::USER_NON_ADMIN; + initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; + delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; + } + + sandbox::ResultCode result = mPolicy->SetJobLevel(jobLevel, + 0 /* ui_exceptions */); + MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result, + "Setting job level failed, have you set memory limit when jobLevel == JOB_NONE?"); + + // 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 > 2) { + result = mPolicy->SetAlternateDesktop(true); + MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result, + "Failed to create alternate desktop for sandbox."); + } + + sandbox::MitigationFlags mitigations = + sandbox::MITIGATION_BOTTOM_UP_ASLR | + sandbox::MITIGATION_HEAP_TERMINATE | + sandbox::MITIGATION_SEHOP | + sandbox::MITIGATION_DEP_NO_ATL_THUNK | + sandbox::MITIGATION_DEP; + + 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 content process needs to be able to duplicate named pipes back to the + // broker process, 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?"); + + // 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?"); +} +#endif + +#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::SetSecurityLevelForPluginProcess(int32_t aSandboxLevel) +{ + if (!mPolicy) { + return false; + } + + sandbox::JobLevel jobLevel; + sandbox::TokenLevel accessTokenLevel; + sandbox::IntegrityLevel initialIntegrityLevel; + sandbox::IntegrityLevel delayedIntegrityLevel; + + if (aSandboxLevel > 2) { + jobLevel = sandbox::JOB_UNPROTECTED; + accessTokenLevel = sandbox::USER_LIMITED; + initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; + delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; + } else if (aSandboxLevel == 2) { + jobLevel = sandbox::JOB_UNPROTECTED; + accessTokenLevel = sandbox::USER_INTERACTIVE; + initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; + delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; + } else { + jobLevel = sandbox::JOB_NONE; + accessTokenLevel = sandbox::USER_NON_ADMIN; + initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM; + delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM; + } + + sandbox::ResultCode result = mPolicy->SetJobLevel(jobLevel, + 0 /* ui_exceptions */); + SANDBOX_ENSURE_SUCCESS(result, + "Setting job level failed, have you set memory limit when jobLevel == JOB_NONE?"); + + result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, + accessTokenLevel); + SANDBOX_ENSURE_SUCCESS(result, + "Lockdown level cannot be USER_UNPROTECTED or USER_LAST if initial level was USER_RESTRICTED_SAME_ACCESS"); + + result = mPolicy->SetIntegrityLevel(initialIntegrityLevel); + SANDBOX_ENSURE_SUCCESS(result, + "SetIntegrityLevel should never fail, what happened?"); + + result = mPolicy->SetDelayedIntegrityLevel(delayedIntegrityLevel); + SANDBOX_ENSURE_SUCCESS(result, + "SetDelayedIntegrityLevel should never fail, what happened?"); + + sandbox::MitigationFlags mitigations = + sandbox::MITIGATION_BOTTOM_UP_ASLR | + sandbox::MITIGATION_HEAP_TERMINATE | + sandbox::MITIGATION_SEHOP | + sandbox::MITIGATION_DEP_NO_ATL_THUNK | + sandbox::MITIGATION_DEP; + + result = mPolicy->SetProcessMitigations(mitigations); + SANDBOX_ENSURE_SUCCESS(result, + "Invalid flags for SetProcessMitigations."); + + // 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 NPAPI process needs to be able to duplicate shared memory to the + // content process and broker process, which are Section type handles. + // Content and broker are for e10s and non-e10s cases. + 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?"); + + 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?"); + + // The following is required for the Java plugin. + result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_ANY, + L"\\??\\pipe\\jpi2_pid*_pipe*"); + SANDBOX_ENSURE_SUCCESS(result, + "With these static arguments AddRule should never fail, what happened?"); + + // These register keys are used by the file-browser dialog box. They + // remember the most-recently-used folders. + result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY, + sandbox::TargetPolicy::REG_ALLOW_ANY, + L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\\*"); + 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_ANY, + L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRULegacy\\*"); + SANDBOX_ENSURE_SUCCESS(result, + "With these static arguments AddRule should never fail, what happened?"); + + return true; +} + +bool +SandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel) +{ + if (!mPolicy) { + return false; + } + + auto result = mPolicy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0); + 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); + SANDBOX_ENSURE_SUCCESS(result, + "Failed to create alternate desktop for sandbox."); + + 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?"); + + sandbox::MitigationFlags mitigations = + sandbox::MITIGATION_BOTTOM_UP_ASLR | + sandbox::MITIGATION_HEAP_TERMINATE | + sandbox::MITIGATION_SEHOP | + sandbox::MITIGATION_DEP_NO_ATL_THUNK | + sandbox::MITIGATION_DEP; + + result = mPolicy->SetProcessMitigations(mitigations); + SANDBOX_ENSURE_SUCCESS(result, + "Invalid flags for SetProcessMitigations."); + + 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_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 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_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\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?"); + + + 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; +} + +bool +SandboxBroker::AllowReadWriteFile(wchar_t const *file) +{ + if (!mPolicy) { + return false; + } + + auto result = + mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_ANY, + file); + if (sandbox::SBOX_ALL_OK != result) { + LOG_E("Failed (ResultCode %d) to add read/write access to: %S", + result, file); + return false; + } + + return true; +} + +bool +SandboxBroker::AllowDirectory(wchar_t const *dir) +{ + if (!mPolicy) { + return false; + } + + auto result = + mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_DIR_ANY, + dir); + if (sandbox::SBOX_ALL_OK != result) { + LOG_E("Failed (ResultCode %d) to add directory access to: %S", result, dir); + return false; + } + + return true; +} + +bool +SandboxBroker::AddTargetPeer(HANDLE aPeerProcess) +{ + if (!sBrokerService) { + return false; + } + + sandbox::ResultCode result = sBrokerService->AddTargetPeer(aPeerProcess); + return (sandbox::SBOX_ALL_OK == result); +} + +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; + } +} + +} diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h new file mode 100644 index 000000000..3f73ec890 --- /dev/null +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h @@ -0,0 +1,63 @@ +/* -*- 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> + +namespace sandbox { + class BrokerServices; + class TargetPolicy; +} + +namespace mozilla { + +class SandboxBroker +{ +public: + SandboxBroker(); + + static void Initialize(sandbox::BrokerServices* aBrokerServices); + + bool LaunchApp(const wchar_t *aPath, + const wchar_t *aArguments, + const bool aEnableLogging, + void **aProcessHandle); + virtual ~SandboxBroker(); + + // Security levels for different types of processes +#if defined(MOZ_CONTENT_SANDBOX) + void SetSecurityLevelForContentProcess(int32_t aSandboxLevel); +#endif + bool SetSecurityLevelForPluginProcess(int32_t aSandboxLevel); + enum SandboxLevel { + LockDown, + Restricted + }; + bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel); + + // File system permissions + bool AllowReadFile(wchar_t const *file); + bool AllowReadWriteFile(wchar_t const *file); + bool AllowDirectory(wchar_t const *dir); + + // Exposes AddTargetPeer from broker services, so that none sandboxed + // processes can be added as handle duplication targets. + bool AddTargetPeer(HANDLE aPeerProcess); + + // Set up dummy interceptions via the broker, so we can log calls. + void ApplyLoggingPolicy(); + +private: + static sandbox::BrokerServices *sBrokerService; + sandbox::TargetPolicy *mPolicy; +}; + +} // 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 000000000..7315a34ed --- /dev/null +++ b/security/sandbox/win/src/sandboxtarget/moz.build @@ -0,0 +1,20 @@ +# -*- 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', +] + +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 000000000..a989b3625 --- /dev/null +++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp @@ -0,0 +1,21 @@ +/* -*- 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/. */ + +#define TARGET_SANDBOX_EXPORTS +#include "sandboxTarget.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; +} + +} diff --git a/security/sandbox/win/src/sandboxtarget/sandboxTarget.h b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h new file mode 100644 index 000000000..f46a2ce70 --- /dev/null +++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h @@ -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 http://mozilla.org/MPL/2.0/. */ + +#ifndef __SECURITY_SANDBOX_SANDBOXTARGET_H__ +#define __SECURITY_SANDBOX_SANDBOXTARGET_H__ + +#include <windows.h> + +#include "mozilla/Assertions.h" +#include "base/MissingBasicTypes.h" +#include "sandbox/win/src/sandbox.h" + +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; + } + + /** + * Called by the library that wants to "start" the sandbox, i.e. change to the + * more secure delayed / lockdown policy. + */ + void StartSandbox() + { + if (mTargetServices) { + mTargetServices->LowerToken(); + } + } + + /** + * 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) + { + if (!mTargetServices) { + return false; + } + + sandbox::ResultCode result = + mTargetServices->DuplicateHandle(aSourceHandle, aTargetProcessId, + aTargetHandle, aDesiredAccess, aOptions); + return (sandbox::SBOX_ALL_OK == result); + } + +protected: + SandboxTarget() : + mTargetServices(nullptr) + { + } + + sandbox::TargetServices* mTargetServices; +}; + + +} // mozilla + +#endif diff --git a/security/sandbox/win/wow_helper/Makefile.in b/security/sandbox/win/wow_helper/Makefile.in new file mode 100644 index 000000000..aee81f3fe --- /dev/null +++ b/security/sandbox/win/wow_helper/Makefile.in @@ -0,0 +1,47 @@ +# 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/. + +# We need to build a 64-bits binary during a 32-bits build. This requires +# a different compiler and different library paths. Until the build system +# supports this natively. + +# Some Make magic to avoid CXX and LIB being evaluated when nothing +# is built in this directory +lazy = $(if $(___$(1)),,$(eval ___$(1) := $(2)))$(___$(1)) + +# We could use the `which` python module, but it needs more code to handle +# the situation where CXX points to an absolute path. But using the shell +# which returns a msys path, while we need a windows path. So force msys +# to do the conversion for us by calling python with an environment variable +# with the result of the call to `which`. Then munge that path to add the +# x64 cross-compiler path. +ifdef MOZ_USING_COMPILER_WRAPPER +ORIG_CXX := cl +else +ORIG_CXX := $(CXX) +endif +CXX = $(call lazy,CXX,"$$(subst amd64_x86/x86_amd64/,amd64/,$$(shell CL=`which "$(ORIG_CXX)"` $(PYTHON) -c 'import os; print os.path.dirname(os.environ["CL"])')/x86_amd64/cl.exe)") + +MOZ_WINCONSOLE = 0 + +include $(topsrcdir)/config/config.mk + +# Munge the LIB variable to contain paths to the x64 CRT and system libraries. +# Unconveniently, none of the paths have the same convention, including the +# compiler path above. +LIB = $(call lazy,LIB,$$(shell python -c 'import os; print ";".join(s.lower().replace(os.sep, "/").replace("/vc/lib", "/vc/lib/amd64").replace("/um/x86", "/um/x64").replace("/ucrt/x86", "/ucrt/x64") for s in os.environ["LIB"].split(";"))')) + +CXXFLAGS := $(filter-out -arch:%,$(CXXFLAGS)) + +# OS_COMPILE_CXXFLAGS includes mozilla-config.h, which contains x86-specific +# defines breaking the build. +OS_COMPILE_CXXFLAGS := + +# LNK1246: '/SAFESEH' not compatible with 'x64' target machine +LDFLAGS := $(filter-out -SAFESEH,$(LDFLAGS)) + +# When targetting x64, we need to specify a subsystem of at least 5.02, because +# the 5.01 value we inherit from the x86 parts is silently ignored, making the +# linker default to 6.00 (Vista) as of VS2013. +WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,5.02 diff --git a/security/sandbox/win/wow_helper/moz.build b/security/sandbox/win/wow_helper/moz.build new file mode 100644 index 000000000..d9c307def --- /dev/null +++ b/security/sandbox/win/wow_helper/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/. + +Program('wow_helper') + +SOURCES += [ '../../chromium/sandbox/win/wow_helper/' + f for f in ( + 'service64_resolver.cc', + 'target_code.cc', + 'wow_helper.cc', +)] + +LOCAL_INCLUDES += [ + '../../', + '../../../', + '../../chromium/', +] + +DISABLE_STL_WRAPPING = True + +DEFINES['UNICODE'] = True + +USE_STATIC_LIBS = True + +# The rules in Makefile.in only force the use of the 64-bits compiler, not +# the 64-bits linker, and the 32-bits linker can't do 64-bits compilation for +# PGO, so disable PGO, which is not interesting for this small binary anyways. +NO_PGO = True |