Bug 1339105 Part 1: Implement Windows Level 3 content process sandbox policy. r=jimm

MozReview-Commit-ID: L8wcVhdLvFe
This commit is contained in:
Bob Owen 2017-05-22 14:29:06 +01:00
Родитель e75f6bd810
Коммит f24abd4ac3
5 изменённых файлов: 124 добавлений и 109 удалений

Просмотреть файл

@ -23,7 +23,7 @@
#include "prenv.h" #include "prenv.h"
#include "nsXPCOMPrivate.h" #include "nsXPCOMPrivate.h"
#if defined(MOZ_CONTENT_SANDBOX) #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_MACOSX)
#include "nsAppDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h"
#endif #endif
@ -48,7 +48,6 @@
#if defined(MOZ_SANDBOX) #if defined(MOZ_SANDBOX)
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/sandboxing/sandboxLogging.h" #include "mozilla/sandboxing/sandboxLogging.h"
#include "nsDirectoryServiceUtils.h"
#endif #endif
#endif #endif
@ -112,34 +111,6 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
#endif #endif
{ {
MOZ_COUNT_CTOR(GeckoChildProcessHost); MOZ_COUNT_CTOR(GeckoChildProcessHost);
#if defined(OS_WIN) && defined(MOZ_CONTENT_SANDBOX)
// Add $PROFILE/chrome to the white list because it may located on network
// drive.
if (mProcessType == GeckoProcessType_Content) {
nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
NS_ASSERTION(directoryService, "Expected XPCOM to be available");
if (directoryService) {
// Full path to the profile dir
nsCOMPtr<nsIFile> profileDir;
nsresult rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(profileDir));
if (NS_SUCCEEDED(rv)) {
profileDir->Append(NS_LITERAL_STRING("chrome"));
profileDir->Append(NS_LITERAL_STRING("*"));
nsAutoCString path;
MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path));
std::wstring wpath = UTF8ToWide(path.get());
// If the patch starts with "\\\\", it is a UNC path.
if (wpath.find(L"\\\\") == 0) {
wpath.insert(1, L"??\\UNC");
}
mAllowedFilesRead.push_back(wpath);
}
}
}
#endif
} }
GeckoChildProcessHost::~GeckoChildProcessHost() GeckoChildProcessHost::~GeckoChildProcessHost()
@ -323,78 +294,6 @@ GeckoChildProcessHost::GetUniqueID()
return sNextUniqueID++; return sNextUniqueID++;
} }
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
// This is pretty much a duplicate of the function in PluginProcessParent.
// Simply copying for now due to uplift. I will address this duplication and for
// example the similar code in the Constructor in bug 1339105.
static void
AddSandboxAllowedFile(std::vector<std::wstring>& aAllowedFiles,
nsIProperties* aDirSvc, const char* aDirKey,
const nsAString& aSuffix = EmptyString())
{
nsCOMPtr<nsIFile> ruleDir;
nsresult rv =
aDirSvc->Get(aDirKey, NS_GET_IID(nsIFile), getter_AddRefs(ruleDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoString rulePath;
rv = ruleDir->GetPath(rulePath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Convert network share path to format for sandbox policy.
if (Substring(rulePath, 0, 2).Equals(L"\\\\")) {
rulePath.InsertLiteral(u"??\\UNC", 1);
}
if (!aSuffix.IsEmpty()) {
rulePath.Append(aSuffix);
}
aAllowedFiles.push_back(std::wstring(rulePath.get()));
return;
}
static void
AddContentSandboxAllowedFiles(int32_t aSandboxLevel,
std::vector<std::wstring>& aAllowedFilesRead,
std::vector<std::wstring>& aAllowedFilesReadWrite)
{
if (aSandboxLevel < 1) {
return;
}
nsresult rv;
nsCOMPtr<nsIProperties> dirSvc =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Add rule to allow read / write access to content temp dir. If for some
// reason the addition of the content temp failed, this will give write access
// to the normal TEMP dir. However such failures should be pretty rare and
// without this printing will not currently work.
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc,
NS_APP_CONTENT_PROCESS_TEMP_DIR,
NS_LITERAL_STRING("\\*"));
if (aSandboxLevel < 2) {
return;
}
// Add rule to allow read access to installation directory. At less than
// level 2 we already add a global read rule.
AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_GRE_DIR,
NS_LITERAL_STRING("\\*"));
}
#endif
void void
GeckoChildProcessHost::PrepareLaunch() GeckoChildProcessHost::PrepareLaunch()
{ {
@ -415,11 +314,6 @@ GeckoChildProcessHost::PrepareLaunch()
mSandboxLevel = Preferences::GetInt("security.sandbox.content.level"); mSandboxLevel = Preferences::GetInt("security.sandbox.content.level");
mEnableSandboxLogging = mEnableSandboxLogging =
Preferences::GetBool("security.sandbox.logging.enabled"); Preferences::GetBool("security.sandbox.logging.enabled");
// This calls the directory service, which can also cause issues if called
// off main thread.
AddContentSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead,
mAllowedFilesReadWrite);
} }
#endif #endif

Просмотреть файл

@ -1462,6 +1462,7 @@ XRE_XPCShellMain(int argc, char** argv, char** envp,
// Required for sandboxed child processes. // Required for sandboxed child processes.
if (aShellData->sandboxBrokerServices) { if (aShellData->sandboxBrokerServices) {
SandboxBroker::Initialize(aShellData->sandboxBrokerServices); SandboxBroker::Initialize(aShellData->sandboxBrokerServices);
SandboxBroker::CacheRulesDirectories();
} else { } else {
NS_WARNING("Failed to initialize broker services, sandboxed " NS_WARNING("Failed to initialize broker services, sandboxed "
"processes will fail to start."); "processes will fail to start.");

Просмотреть файл

@ -8,8 +8,17 @@
#include "base/win/windows_version.h" #include "base/win/windows_version.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Logging.h" #include "mozilla/Logging.h"
#include "mozilla/NSPRLogModulesParser.h" #include "mozilla/NSPRLogModulesParser.h"
#include "mozilla/UniquePtr.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCOMPtr.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsIProperties.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox.h"
#include "sandbox/win/src/security_level.h" #include "sandbox/win/src/security_level.h"
@ -18,6 +27,11 @@ namespace mozilla
sandbox::BrokerServices *SandboxBroker::sBrokerService = nullptr; sandbox::BrokerServices *SandboxBroker::sBrokerService = nullptr;
// Cached special directories used for adding policy rules.
static UniquePtr<nsString> sBinDir;
static UniquePtr<nsString> sProfileDir;
static UniquePtr<nsString> sContentTempDir;
static LazyLogModule sSandboxBrokerLog("SandboxBroker"); static LazyLogModule sSandboxBrokerLog("SandboxBroker");
#define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__)) #define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__))
@ -30,6 +44,50 @@ SandboxBroker::Initialize(sandbox::BrokerServices* aBrokerServices)
sBrokerService = aBrokerServices; sBrokerService = aBrokerServices;
} }
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(L"\\\\")) {
(*cacheVar)->InsertLiteral(u"??\\UNC", 1);
}
}
/* static */
void
SandboxBroker::CacheRulesDirectories()
{
MOZ_ASSERT(NS_IsMainThread());
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_APP_CONTENT_PROCESS_TEMP_DIR, &sContentTempDir);
}
SandboxBroker::SandboxBroker() SandboxBroker::SandboxBroker()
{ {
if (sBrokerService) { if (sBrokerService) {
@ -133,7 +191,35 @@ SandboxBroker::LaunchApp(const wchar_t *aPath,
return true; return true;
} }
static void
AddCachedDirRule(sandbox::TargetPolicy* aPolicy,
sandbox::TargetPolicy::Semantics aAccess,
const UniquePtr<nsString>& aBaseDir,
const nsAString& 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",
aRelativePath, 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, rulePath);
}
}
#if defined(MOZ_CONTENT_SANDBOX) #if defined(MOZ_CONTENT_SANDBOX)
void void
SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel, SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
base::ChildPrivileges aPrivs) base::ChildPrivileges aPrivs)
@ -159,7 +245,12 @@ SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
accessTokenLevel = sandbox::USER_LIMITED; accessTokenLevel = sandbox::USER_LIMITED;
initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
} else if (aSandboxLevel >= 2) { } 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; jobLevel = sandbox::JOB_INTERACTIVE;
accessTokenLevel = sandbox::USER_INTERACTIVE; accessTokenLevel = sandbox::USER_INTERACTIVE;
initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW; initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
@ -204,7 +295,7 @@ SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result, MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
"SetDelayedIntegrityLevel should never fail, what happened?"); "SetDelayedIntegrityLevel should never fail, what happened?");
if (aSandboxLevel > 2) { if (aSandboxLevel > 3) {
result = mPolicy->SetAlternateDesktop(true); result = mPolicy->SetAlternateDesktop(true);
MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result, MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
"Failed to create alternate desktop for sandbox."); "Failed to create alternate desktop for sandbox.");
@ -229,6 +320,13 @@ SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result, MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
"Invalid flags for SetDelayedProcessMitigations."); "Invalid flags for SetDelayedProcessMitigations.");
// Add rule to allow read / write access to content temp dir. If for some
// reason the addition of the content temp failed, this will give write access
// to the normal TEMP dir. However such failures should be pretty rare and
// without this printing will not currently work.
AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_ANY,
sContentTempDir, NS_LITERAL_STRING("\\*"));
// We still have edge cases where the child at low integrity can't read some // 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. // files, so add a rule to allow read access to everything when required.
if (aSandboxLevel == 1 || if (aSandboxLevel == 1 ||
@ -238,6 +336,18 @@ SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
L"*"); L"*");
MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result, MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
"With these static arguments AddRule should never fail, what happened?"); "With these static arguments AddRule should never fail, what happened?");
} else {
// Add rule to allow read access to installation directory.
AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
sBinDir, NS_LITERAL_STRING("\\*"));
// Add rule to allow read access chrome directory within profile.
AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
sProfileDir, NS_LITERAL_STRING("\\chrome\\*"));
// Add rule to allow read access extensions directory within profile.
AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
sProfileDir, NS_LITERAL_STRING("\\extensions\\*"));
} }
// Add the policy for the client side of a pipe. It is just a file // Add the policy for the client side of a pipe. It is just a file

Просмотреть файл

@ -26,6 +26,12 @@ public:
static void Initialize(sandbox::BrokerServices* aBrokerServices); static void Initialize(sandbox::BrokerServices* aBrokerServices);
/**
* Cache directory paths for use in policy rules. Must be called on main
* thread.
*/
static void CacheRulesDirectories();
bool LaunchApp(const wchar_t *aPath, bool LaunchApp(const wchar_t *aPath,
const wchar_t *aArguments, const wchar_t *aArguments,
const bool aEnableLogging, const bool aEnableLogging,

Просмотреть файл

@ -4441,6 +4441,10 @@ XREMain::XRE_mainRun()
// We intentionally leak the string here since it is required by PR_SetEnv. // We intentionally leak the string here since it is required by PR_SetEnv.
PR_SetEnv(saved.release()); PR_SetEnv(saved.release());
} }
// Call SandboxBroker to cache directories needed for policy rules, this must
// be called after mDirProvider.DoStartup as it needs the profile dir.
SandboxBroker::CacheRulesDirectories();
#endif #endif
SaveStateForAppInitiatedRestart(); SaveStateForAppInitiatedRestart();