Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#include "ConfigHelpers.h"
#include <windows.h>
#include "mozilla/Logging.h"
#include "mozilla/Vector.h"
#include "nsExceptionHandler.h"
#include "nsStringFwd.h"
#include "nsUnicharUtils.h"
#include "nsWindowsHelpers.h"
#include "sandbox/win/src/policy_engine_opcodes.h"
namespace mozilla {
extern LazyLogModule sSandboxBrokerLog;
#define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__))
#define LOG_W(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Warning, (__VA_ARGS__))
namespace sandboxing {
SizeTrackingConfig::SizeTrackingConfig(sandbox::TargetConfig* aConfig,
int32_t aStoragePages)
: mConfig(aConfig) {
MOZ_ASSERT(mConfig);
// The calculation uses the kPolMemPageCount constant in sandbox_policy.h.
// We reduce the allowable size by 1 to account for the PolicyGlobal.
MOZ_ASSERT(aStoragePages > 0);
MOZ_ASSERT(static_cast<size_t>(aStoragePages) < sandbox::kPolMemPageCount);
constexpr int32_t kOneMemPage = 4096;
mRemainingSize = kOneMemPage * aStoragePages;
}
sandbox::ResultCode SizeTrackingConfig::AllowFileAccess(
sandbox::FileSemantics aSemantics, const wchar_t* aPattern) {
// This calculation doesn't allow for wild-cards, pipes or things that have an
// an NT prefix, but in our use cases this would result in an overestimate, so
// that is fine for our purposes. Wild-cards mid pattern would be undersized,
// because of extra opcodes, but we don't have any rules like these.
// Add 4 to length to allow for \??\ NT prefix added to most rules. The
// pattern is stored with a length and so the null-terminator is not stored.
auto patternRuleSize = (wcslen(aPattern) + 4) * sizeof(wchar_t);
// Each brokered function has a copy of the pattern and a number of opcodes
// depending on aSemantics. This is generally 1 opcode for the string match
// and 1 for the action (ASK_BROKER) opcode added when Done is called on the
// rule. For kAllowReadonly access and disposition checks are also added for
// create and open making 4 opcodes in total.
int32_t requiredSize;
constexpr auto opcodeSize = sizeof(sandbox::PolicyOpcode);
switch (aSemantics) {
case sandbox::FileSemantics::kAllowAny:
// create, open, query, query_full and rename brokered with 2 opcodes.
requiredSize = (patternRuleSize * 5) + (opcodeSize * 10);
break;
case sandbox::FileSemantics::kAllowReadonly:
// create and open brokered with 4 opcodes
// query and query_full brokered with 2 opcodes
requiredSize = (patternRuleSize * 4) + (opcodeSize * 12);
break;
case sandbox::FileSemantics::kAllowQuery:
// query and query_full brokered with 2 opcodes
requiredSize = (patternRuleSize * 2) + (opcodeSize * 4);
break;
default:
MOZ_CRASH("Unknown FileSemantics");
}
if (requiredSize > mRemainingSize) {
return sandbox::SBOX_ERROR_NO_SPACE;
}
mRemainingSize -= requiredSize;
return mConfig->AllowFileAccess(aSemantics, aPattern);
}
UserFontConfigHelper::UserFontConfigHelper(const wchar_t* aUserFontKeyPath,
const nsString& aWinUserProfile,
const nsString& aLocalAppData)
: mWinUserProfile(aWinUserProfile), mLocalAppData(aLocalAppData) {
LSTATUS lStatus =
::RegOpenKeyExW(HKEY_CURRENT_USER, aUserFontKeyPath, 0,
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &mUserFontKey);
if (lStatus != ERROR_SUCCESS) {
// Ensure that mUserFontKey is null on failure.
mUserFontKey = nullptr;
}
}
UserFontConfigHelper::~UserFontConfigHelper() {
if (mUserFontKey) {
::RegCloseKey(mUserFontKey);
}
}
static auto AddRulesForKey(HKEY aFontKey, const nsAString& aWindowsUserFontDir,
const nsAString& aWinUserProfile,
SizeTrackingConfig& aConfig,
Vector<nsString>& aNonUserDirFonts) {
for (DWORD valueIndex = 0; /* break on ERROR_NO_MORE_ITEMS */; ++valueIndex) {
DWORD keyType;
wchar_t name[1024];
wchar_t data[2048];
auto* dataAsBytes = reinterpret_cast<LPBYTE>(data);
DWORD nameLength = std::size(name);
// Pass 1 less wchar_t worth, in case we have to add a null.
DWORD dataSizeInBytes = sizeof(data) - sizeof(wchar_t);
LSTATUS lStatus =
::RegEnumValueW(aFontKey, valueIndex, name, &nameLength, NULL, &keyType,
dataAsBytes, &dataSizeInBytes);
if (lStatus == ERROR_NO_MORE_ITEMS) {
break;
}
// Skip if we failed to retrieve the value.
if (lStatus != ERROR_SUCCESS) {
continue;
}
// Only strings are used, REG_EXPAND_SZ is not recognized by Fonts panel.
if (keyType != REG_SZ) {
continue;
}
auto dataSizeInWChars = dataSizeInBytes / sizeof(wchar_t);
// We test data[dataSizeInWChars - 1] below and might test again after
// decrementing it, so ensure we have at least 2 wchar_ts. A valid font path
// couldn't be this short anyway.
if (dataSizeInWChars < 2) {
continue;
}
// Size might include a null.
if (data[dataSizeInWChars - 1] == L'\0') {
--dataSizeInWChars;
} else {
// Ensure null terminated.
data[dataSizeInWChars] = L'\0';
}
// Should be path to font file so reject directories.
if (data[dataSizeInWChars - 1] == L'\\') {
continue;
}
// If not in the user's dir, store until the end.
if (dataSizeInWChars < aWinUserProfile.Length() ||
!aWinUserProfile.Equals(
nsDependentSubstring(data, aWinUserProfile.Length()),
nsCaseInsensitiveStringComparator)) {
(void)aNonUserDirFonts.emplaceBack(data, dataSizeInWChars);
continue;
}
// Skip if in windows user font dir.
if (dataSizeInWChars > aWindowsUserFontDir.Length() &&
aWindowsUserFontDir.Equals(
nsDependentSubstring(data, aWindowsUserFontDir.Length()),
nsCaseInsensitiveStringComparator)) {
continue;
}
auto result =
aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly, data);
if (result != sandbox::SBOX_ALL_OK) {
NS_WARNING("Failed to add specific user font policy rule.");
LOG_W("Failed (ResultCode %d) to add read access to: %S", result, data);
if (result == sandbox::SBOX_ERROR_NO_SPACE) {
return result;
}
}
}
for (DWORD keyIndex = 0; /* break on ERROR_NO_MORE_ITEMS */; ++keyIndex) {
wchar_t name[1024];
DWORD nameLength = std::size(name);
LSTATUS lStatus = ::RegEnumKeyExW(aFontKey, keyIndex, name, &nameLength,
nullptr, nullptr, nullptr, nullptr);
if (lStatus == ERROR_NO_MORE_ITEMS) {
break;
}
// Skip if we failed to retrieve the key name.
if (lStatus != ERROR_SUCCESS) {
continue;
}
std::unique_ptr<HKEY, RegCloseKeyDeleter> subKey;
lStatus = ::RegOpenKeyExW(aFontKey, name, 0,
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
getter_Transfers(subKey));
// Skip if we failed to retrieve the key.
if (lStatus != ERROR_SUCCESS) {
continue;
}
auto result = AddRulesForKey(subKey.get(), aWindowsUserFontDir,
aWinUserProfile, aConfig, aNonUserDirFonts);
if (result == sandbox::SBOX_ERROR_NO_SPACE) {
return result;
}
}
return sandbox::SBOX_ALL_OK;
}
void UserFontConfigHelper::AddRules(SizeTrackingConfig& aConfig) const {
// Always add rule to allow access to windows user specific fonts dir first.
nsAutoString windowsUserFontDir(mLocalAppData);
windowsUserFontDir += uR"(\Microsoft\Windows\Fonts\*)"_ns;
auto result = aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly,
windowsUserFontDir.getW());
if (result != sandbox::SBOX_ALL_OK) {
NS_ERROR("Failed to add Windows user font dir policy rule.");
LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
windowsUserFontDir.getW());
}
// We failed to open the registry key, we can't do any more.
if (!mUserFontKey) {
return;
}
// We don't want the wild-card for comparisons.
windowsUserFontDir.SetLength(windowsUserFontDir.Length() - 1);
// Windows user's profile dir with trailing slash for comparisons.
nsAutoString winUserProfile(mWinUserProfile);
winUserProfile += L'\\';
// We will add rules for fonts outside of the User's directory at the end, in
// case we run out of space.
Vector<nsString> nonUserDirFonts;
result = AddRulesForKey(mUserFontKey, windowsUserFontDir, winUserProfile,
aConfig, nonUserDirFonts);
if (result == sandbox::SBOX_ERROR_NO_SPACE) {
CrashReporter::RecordAnnotationCString(
CrashReporter::Annotation::UserFontRulesExhausted, "inside");
return;
}
// Finally add rules for fonts outside the user's dir. These are less likely
// to have access blocked by USER_LIMITED.
for (const auto& fontPath : nonUserDirFonts) {
result = aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly,
fontPath.getW());
if (result != sandbox::SBOX_ALL_OK) {
NS_WARNING("Failed to add specific user font policy rule.");
LOG_W("Failed (ResultCode %d) to add read access to: %S", result,
fontPath.getW());
if (result == sandbox::SBOX_ERROR_NO_SPACE) {
CrashReporter::RecordAnnotationCString(
CrashReporter::Annotation::UserFontRulesExhausted, "outside");
return;
}
}
}
}
} // namespace sandboxing
} // namespace mozilla