From e9bcaf4cbec28c3aa0977b91ca26aca3708228bd Mon Sep 17 00:00:00 2001 From: David Parks Date: Tue, 14 Feb 2017 15:08:40 -0800 Subject: [PATCH] Bug 1284897 - Add mechanism to libsandbox_s to track names of files that have been given special sandbox access permissions (PermissionsService). r=bobowen, r=glandium Hook this into the browser via the XREAppData. This patch does not include the changes to Chromium source code. --- browser/app/nsBrowserApp.cpp | 3 + .../sandbox/win/permissionsService.cpp | 136 ++++++++++++++++++ .../sandbox/win/permissionsService.h | 78 ++++++++++ security/sandbox/moz.build | 3 + .../sandbox/win/SandboxInitialization.cpp | 6 + security/sandbox/win/SandboxInitialization.h | 4 + .../win/src/sandboxpermissions/moz.build | 22 +++ .../sandboxpermissions/sandboxPermissions.cpp | 39 +++++ .../sandboxpermissions/sandboxPermissions.h | 57 ++++++++ toolkit/xre/Bootstrap.h | 7 + toolkit/xre/nsAppRunner.cpp | 6 + xpcom/build/XREAppData.h | 6 + xpcom/glue/XREAppData.cpp | 1 + 13 files changed, 368 insertions(+) create mode 100644 security/sandbox/chromium-shim/sandbox/win/permissionsService.cpp create mode 100644 security/sandbox/chromium-shim/sandbox/win/permissionsService.h create mode 100644 security/sandbox/win/src/sandboxpermissions/moz.build create mode 100644 security/sandbox/win/src/sandboxpermissions/sandboxPermissions.cpp create mode 100644 security/sandbox/win/src/sandboxpermissions/sandboxPermissions.h diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp index d6a6fff66748..6a5945307d47 100644 --- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -217,6 +217,8 @@ static int do_main(int argc, char* argv[], char* envp[]) #if defined(XP_WIN) && defined(MOZ_SANDBOX) sandbox::BrokerServices* brokerServices = sandboxing::GetInitializedBrokerServices(); + sandboxing::PermissionsService* permissionsService = + sandboxing::GetPermissionsService(); #if defined(MOZ_CONTENT_SANDBOX) if (!brokerServices) { Output("Couldn't initialize the broker services.\n"); @@ -224,6 +226,7 @@ static int do_main(int argc, char* argv[], char* envp[]) } #endif config.sandboxBrokerServices = brokerServices; + config.sandboxPermissionsService = permissionsService; #endif #ifdef LIBFUZZER diff --git a/security/sandbox/chromium-shim/sandbox/win/permissionsService.cpp b/security/sandbox/chromium-shim/sandbox/win/permissionsService.cpp new file mode 100644 index 000000000000..13db7ca4ee84 --- /dev/null +++ b/security/sandbox/chromium-shim/sandbox/win/permissionsService.cpp @@ -0,0 +1,136 @@ +/* -*- 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/. */ + +/* SandboxPermissions.cpp - Special permissions granted to sandboxed processes */ + +#include "permissionsService.h" +#include +#include + +namespace mozilla { +namespace sandboxing { + +static const std::wstring ZONE_IDENTIFIER_STR(L":ZONE.IDENTIFIER"); +static const std::wstring ZONE_ID_DATA_STR(L":ZONE.IDENTIFIER:$DATA"); + +bool +StringEndsWith(const std::wstring& str, const std::wstring& strEnding) +{ + if (strEnding.size() > str.size()) { + return false; + } + return std::equal(strEnding.rbegin(), strEnding.rend(), str.rbegin()); +} + +// Converts NT device internal filenames to normal user-space by stripping +// the prefixes and suffixes from the file name. +std::wstring +GetPlainFileName(const wchar_t* aNTFileName) +{ + while (*aNTFileName == L'\\' || *aNTFileName == L'.' || + *aNTFileName == L'?' || *aNTFileName == L':' ) { + ++aNTFileName; + } + std::wstring nameCopy(aNTFileName); + std::transform(nameCopy.begin(), nameCopy.end(), nameCopy.begin(), towupper); + if (StringEndsWith(nameCopy, ZONE_ID_DATA_STR)) { + nameCopy = nameCopy.substr(0, nameCopy.size() - ZONE_ID_DATA_STR.size()); + } else if (StringEndsWith(nameCopy, ZONE_IDENTIFIER_STR)) { + nameCopy = nameCopy.substr(0, nameCopy.size() - ZONE_IDENTIFIER_STR.size()); + } + return nameCopy; +} + +/* static */ PermissionsService* +PermissionsService::GetInstance() +{ + static PermissionsService sPermissionsService; + return &sPermissionsService; +} + +PermissionsService::PermissionsService() : + mFileAccessViolationFunc(nullptr) +{ +} + +void +PermissionsService::GrantFileAccess(uint32_t aProcessId, + const wchar_t* aFilename, + bool aPermitWrite) +{ + FilePermissionMap& permissions = mProcessFilePermissions[aProcessId]; + std::wstring filename = GetPlainFileName(aFilename); + permissions[filename] |= aPermitWrite; +} + +void +PermissionsService::SetFileAccessViolationFunc(FileAccessViolationFunc aFavFunc) +{ + mFileAccessViolationFunc = aFavFunc; +} + +void +PermissionsService::ReportBlockedFile(bool aNeedsWrite) +{ + if (mFileAccessViolationFunc) { + mFileAccessViolationFunc(aNeedsWrite); + } +} + +bool +PermissionsService::UserGrantedFileAccess(uint32_t aProcessId, + const wchar_t* aFilename, + uint32_t aAccess, + uint32_t aDisposition) +{ + // There are 3 types of permissions: + // * Those available w/ read-only permission + // * Those available w/ read-only AND read-write permission + // * Those always forbidden. + const uint32_t FORBIDDEN_FLAGS = + FILE_EXECUTE | FILE_LIST_DIRECTORY | FILE_TRAVERSE | STANDARD_RIGHTS_EXECUTE; + const uint32_t NEEDS_WRITE_FLAGS = + FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | + DELETE | STANDARD_RIGHTS_WRITE; + bool needsWrite = + (aAccess & NEEDS_WRITE_FLAGS) || (aDisposition != FILE_OPEN); + + if (aAccess & FORBIDDEN_FLAGS) { + ReportBlockedFile(needsWrite); + return false; + } + + auto permissions = mProcessFilePermissions.find(aProcessId); + if (permissions == mProcessFilePermissions.end()) { + ReportBlockedFile(needsWrite); + return false; // process has no special file access at all + } + + std::wstring filename = GetPlainFileName(aFilename); + auto itPermission = permissions->second.find(filename); + if (itPermission == permissions->second.end()) { + ReportBlockedFile(needsWrite); + return false; // process has no access to this file + } + + // We have read permission. Check for write permission if requested. + if (!needsWrite || itPermission->second) { + return true; + } + + // We needed write access but didn't have it. + ReportBlockedFile(needsWrite); + return false; +} + +void +PermissionsService::RemovePermissionsForProcess(uint32_t aProcessId) +{ + mProcessFilePermissions.erase(aProcessId); +} + +} // namespace sandboxing +} // namespace mozilla diff --git a/security/sandbox/chromium-shim/sandbox/win/permissionsService.h b/security/sandbox/chromium-shim/sandbox/win/permissionsService.h new file mode 100644 index 000000000000..8e8eefdb8c14 --- /dev/null +++ b/security/sandbox/chromium-shim/sandbox/win/permissionsService.h @@ -0,0 +1,78 @@ +/* -*- 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 mozilla_sandboxing_permissionsService_h +#define mozilla_sandboxing_permissionsService_h + +#include + +namespace mozilla { +namespace sandboxing { + +/* + * Represents additional permissions granted to sandboxed processes. + * The members are virtual so that the object can be created in any + * library that links with libsandbox_s and then shared with and used + * by libXUL, which does not link with libsandbox_s. + */ +class PermissionsService +{ +public: + static PermissionsService* GetInstance(); + + /* + * Allow future access to aFilename by the plugin process. + */ + virtual void GrantFileAccess(uint32_t aProcessId, const wchar_t* aFilename, + bool aPermitWrite); + + /* + * Type of callback function that the sandbox uses to report file + * accesses that were denied. + * Parameter is a boolean indicating the access request was read-only + * (false) or read-write (true) + */ + typedef void (*FileAccessViolationFunc)(bool); + + /* + * Sets the callback function that is called whenever a file access is + * denied by the sandbox. + */ + virtual void SetFileAccessViolationFunc(FileAccessViolationFunc aFavFunc); + + /* + * Returns true if the user has granted the sandboxed plugin process the + * requested permission to open the file. + * Calls aFavFunc with file info if the file access was blocked. + */ + virtual bool UserGrantedFileAccess(uint32_t aProcessId, const wchar_t* aFilename, + uint32_t aAccess, uint32_t aDisposition); + + /* + * Clears all special file access for the given plugin process. + */ + virtual void RemovePermissionsForProcess(uint32_t aProcessId); + +private: + PermissionsService(); + void ReportBlockedFile(bool aNeedsWrite); + + // Maps from filenames to a boolean indicating read-only permission (false) or + // read-write permission (true). + typedef std::unordered_map FilePermissionMap; + + // Maps from process ID to map of user-granted file permissions for + // that process. + typedef std::unordered_map ProcessFilePermissionMap; + + ProcessFilePermissionMap mProcessFilePermissions; + FileAccessViolationFunc mFileAccessViolationFunc; +}; + +} // namespace sandboxing +} // namespace mozilla + +#endif // mozilla_sandboxing_permissionsService_h diff --git a/security/sandbox/moz.build b/security/sandbox/moz.build index 1420d6498af9..2c93a7fda5ca 100644 --- a/security/sandbox/moz.build +++ b/security/sandbox/moz.build @@ -19,6 +19,7 @@ elif CONFIG['OS_ARCH'] == 'WINNT': DIRS += [ 'win/src/sandboxbroker', + 'win/src/sandboxpermissions', 'win/src/sandboxtarget', ] @@ -29,6 +30,7 @@ elif CONFIG['OS_ARCH'] == 'WINNT': EXPORTS.mozilla.sandboxing += [ 'chromium-shim/sandbox/win/loggingCallbacks.h', 'chromium-shim/sandbox/win/loggingTypes.h', + 'chromium-shim/sandbox/win/permissionsService.h', 'chromium-shim/sandbox/win/sandboxLogging.h', 'win/SandboxInitialization.h', ] @@ -36,6 +38,7 @@ elif CONFIG['OS_ARCH'] == 'WINNT': SOURCES += [ 'chromium-shim/base/files/file_path.cpp', 'chromium-shim/base/logging.cpp', + 'chromium-shim/sandbox/win/permissionsService.cpp', 'chromium-shim/sandbox/win/sandboxLogging.cpp', 'chromium/base/at_exit.cc', 'chromium/base/base_switches.cc', diff --git a/security/sandbox/win/SandboxInitialization.cpp b/security/sandbox/win/SandboxInitialization.cpp index e587c2598307..9b773955c781 100644 --- a/security/sandbox/win/SandboxInitialization.cpp +++ b/security/sandbox/win/SandboxInitialization.cpp @@ -7,6 +7,7 @@ #include "SandboxInitialization.h" #include "sandbox/win/src/sandbox_factory.h" +#include "mozilla/sandboxing/permissionsService.h" namespace mozilla { namespace sandboxing { @@ -77,5 +78,10 @@ GetInitializedBrokerServices() return sInitializedBrokerServices; } +PermissionsService* GetPermissionsService() +{ + return PermissionsService::GetInstance(); +} + } // sandboxing } // mozilla diff --git a/security/sandbox/win/SandboxInitialization.h b/security/sandbox/win/SandboxInitialization.h index e5be08904f41..8ef926fb91f4 100644 --- a/security/sandbox/win/SandboxInitialization.h +++ b/security/sandbox/win/SandboxInitialization.h @@ -21,6 +21,8 @@ namespace mozilla { // sandbox for our namespace painful. namespace sandboxing { +class PermissionsService; + /** * Initializes (if required) and returns the Chromium sandbox TargetServices. * @@ -41,6 +43,8 @@ void LowerSandbox(); */ sandbox::BrokerServices* GetInitializedBrokerServices(); +PermissionsService* GetPermissionsService(); + } // sandboxing } // mozilla diff --git a/security/sandbox/win/src/sandboxpermissions/moz.build b/security/sandbox/win/src/sandboxpermissions/moz.build new file mode 100644 index 000000000000..5a802b79c11e --- /dev/null +++ b/security/sandbox/win/src/sandboxpermissions/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/. + +SOURCES += [ + 'sandboxPermissions.cpp', +] + +EXPORTS += [ + 'sandboxPermissions.h', +] + +for var in ('UNICODE', '_UNICODE'): + DEFINES[var] = True + +LOCAL_INCLUDES += [ + '/security/sandbox/win', +] + +FINAL_LIBRARY = 'xul' diff --git a/security/sandbox/win/src/sandboxpermissions/sandboxPermissions.cpp b/security/sandbox/win/src/sandboxpermissions/sandboxPermissions.cpp new file mode 100644 index 000000000000..7efad56de906 --- /dev/null +++ b/security/sandbox/win/src/sandboxpermissions/sandboxPermissions.cpp @@ -0,0 +1,39 @@ +/* -*- 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 "sandboxPermissions.h" +#include "mozilla/Assertions.h" +#include "mozilla/sandboxing/permissionsService.h" + +namespace mozilla +{ + +sandboxing::PermissionsService* SandboxPermissions::sPermissionsService = nullptr; + +void +SandboxPermissions::Initialize(sandboxing::PermissionsService* aPermissionsService, + FileAccessViolationFunc aFileAccessViolationFunc) +{ + sPermissionsService = aPermissionsService; + sPermissionsService->SetFileAccessViolationFunc(aFileAccessViolationFunc); +} + +void +SandboxPermissions::GrantFileAccess(uint32_t aProcessId, const wchar_t* aFilename, + bool aPermitWrite) +{ + MOZ_ASSERT(sPermissionsService, "Must initialize sandbox PermissionsService"); + sPermissionsService->GrantFileAccess(aProcessId, aFilename, aPermitWrite); +} + +void +SandboxPermissions::RemovePermissionsForProcess(uint32_t aProcessId) +{ + MOZ_ASSERT(sPermissionsService, "Must initialize sandbox PermissionsService"); + sPermissionsService->RemovePermissionsForProcess(aProcessId); +} + +} // namespace mozilla diff --git a/security/sandbox/win/src/sandboxpermissions/sandboxPermissions.h b/security/sandbox/win/src/sandboxpermissions/sandboxPermissions.h new file mode 100644 index 000000000000..4e71e51670a4 --- /dev/null +++ b/security/sandbox/win/src/sandboxpermissions/sandboxPermissions.h @@ -0,0 +1,57 @@ +/* -*- 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 mozilla_sandboxing_sandboxPermissions_h +#define mozilla_sandboxing_sandboxPermissions_h + +#include +#include + +namespace mozilla { + +namespace sandboxing { + class PermissionsService; +} + +/* + * This object wraps a PermissionsService object. This object is available + * in libXUL but PermissionsService is not. + */ +class SandboxPermissions +{ +public: + /* + * Type of callback function that the sandbox uses to report file + * accesses that were denied. + * Parameter is a boolean indicating the access request was read-only + * (false) or read-write (true) + */ + typedef void (*FileAccessViolationFunc)(bool); + + /* + * Prepare this object by providing it with the internal permissions service. + */ + static void Initialize(sandboxing::PermissionsService* aPermissionsService, + FileAccessViolationFunc aFileAccessViolationFunc); + + /* + * Allow future access to aFilename by the process with the given ID. + */ + void GrantFileAccess(uint32_t aProcessId, const wchar_t* aFilename, + bool aPermitWrite); + + /* + * Clears all special file access for the given process. + */ + void RemovePermissionsForProcess(uint32_t aProcessId); + +private: + static sandboxing::PermissionsService* sPermissionsService; +}; + +} // mozilla + +#endif // mozilla_sandboxing_sandboxPermissions_h diff --git a/toolkit/xre/Bootstrap.h b/toolkit/xre/Bootstrap.h index e0a8d02e6776..7d80999eac1a 100644 --- a/toolkit/xre/Bootstrap.h +++ b/toolkit/xre/Bootstrap.h @@ -31,11 +31,18 @@ class BrokerServices; namespace mozilla { +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +namespace sandboxing { +class PermissionsService; +} +#endif + struct BootstrapConfig { #if defined(XP_WIN) && defined(MOZ_SANDBOX) /* Chromium sandbox BrokerServices. */ sandbox::BrokerServices* sandboxBrokerServices; + sandboxing::PermissionsService* sandboxPermissionsService; #endif /* Pointer to static XRE AppData from application.ini.h */ const StaticXREAppData* appData; diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index f23907167f8b..0f33dddb0014 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -214,6 +214,7 @@ #include "mozilla/SandboxInfo.h" #elif defined(XP_WIN) #include "SandboxBroker.h" +#include "SandboxPermissions.h" #endif #endif @@ -3318,6 +3319,10 @@ XREMain::XRE_mainInit(bool* aExitFlag) NS_WARNING("Failed to initialize broker services, sandboxed processes will " "fail to start."); } + if (mAppData->sandboxPermissionsService) { + SandboxPermissions::Initialize(mAppData->sandboxPermissionsService, + nullptr); + } #endif #ifdef XP_MACOSX @@ -4606,6 +4611,7 @@ XREMain::XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig) #if defined(XP_WIN) && defined(MOZ_SANDBOX) mAppData->sandboxBrokerServices = aConfig.sandboxBrokerServices; + mAppData->sandboxPermissionsService = aConfig.sandboxPermissionsService; #endif mozilla::IOInterposerInit ioInterposerGuard; diff --git a/xpcom/build/XREAppData.h b/xpcom/build/XREAppData.h index b94946fec2e8..df3e6b8d1e50 100644 --- a/xpcom/build/XREAppData.h +++ b/xpcom/build/XREAppData.h @@ -17,6 +17,11 @@ namespace sandbox { class BrokerServices; } +namespace mozilla { +namespace sandboxing { +class PermissionsService; +} +} #endif namespace mozilla { @@ -194,6 +199,7 @@ public: * Chromium sandbox BrokerServices. */ sandbox::BrokerServices* sandboxBrokerServices = nullptr; + mozilla::sandboxing::PermissionsService* sandboxPermissionsService; #endif }; diff --git a/xpcom/glue/XREAppData.cpp b/xpcom/glue/XREAppData.cpp index 8aef2e16c043..130ef4ec2d17 100644 --- a/xpcom/glue/XREAppData.cpp +++ b/xpcom/glue/XREAppData.cpp @@ -49,6 +49,7 @@ XREAppData::operator=(const XREAppData& aOther) UAName = aOther.UAName; #if defined(XP_WIN) && defined(MOZ_SANDBOX) sandboxBrokerServices = aOther.sandboxBrokerServices; + sandboxPermissionsService = aOther.sandboxPermissionsService; #endif return *this; }