tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ConfigHelpers.cpp (11388B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ConfigHelpers.h"
      8 
      9 #include <windows.h>
     10 
     11 #include "mozilla/Logging.h"
     12 #include "mozilla/Vector.h"
     13 #include "nsExceptionHandler.h"
     14 #include "nsStringFwd.h"
     15 #include "nsUnicharUtils.h"
     16 #include "nsWindowsHelpers.h"
     17 #include "sandbox/win/src/policy_engine_opcodes.h"
     18 
     19 namespace mozilla {
     20 
     21 extern LazyLogModule sSandboxBrokerLog;
     22 
     23 #define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__))
     24 #define LOG_W(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Warning, (__VA_ARGS__))
     25 
     26 namespace sandboxing {
     27 
     28 SizeTrackingConfig::SizeTrackingConfig(sandbox::TargetConfig* aConfig,
     29                                       int32_t aStoragePages)
     30    : mConfig(aConfig) {
     31  MOZ_ASSERT(mConfig);
     32 
     33  // The calculation uses the kPolMemPageCount constant in sandbox_policy.h.
     34  // We reduce the allowable size by 2 to account for the PolicyGlobal and
     35  // padding that occurs during LowLevelPolicy::Done. See bug 2009140.
     36  MOZ_ASSERT(aStoragePages > 0);
     37  MOZ_ASSERT(static_cast<size_t>(aStoragePages) <=
     38             sandbox::kPolMemPageCount - 2);
     39 
     40  constexpr int32_t kOneMemPage = 4096;
     41  mRemainingSize = kOneMemPage * aStoragePages;
     42 }
     43 
     44 sandbox::ResultCode SizeTrackingConfig::AllowFileAccess(
     45    sandbox::FileSemantics aSemantics, const wchar_t* aPattern) {
     46  // This calculation doesn't allow for wild-cards, pipes or things that have an
     47  // an NT prefix, but in our use cases this would result in an overestimate, so
     48  // that is fine for our purposes. Wild-cards mid pattern would be undersized,
     49  // because of extra opcodes, but we don't have any rules like these.
     50 
     51  // Add 4 to length to allow for \??\ NT prefix added to most rules. The
     52  // pattern is stored with a length and so the null-terminator is not stored.
     53  auto patternRuleSize = (wcslen(aPattern) + 4) * sizeof(wchar_t);
     54 
     55  // Each brokered function has a copy of the pattern and a number of opcodes
     56  // depending on aSemantics. This is generally 1 opcode for the string match
     57  // and 1 for the action (ASK_BROKER) opcode added when Done is called on the
     58  // rule. For kAllowReadonly access and disposition checks are also added for
     59  // create and open making 4 opcodes in total.
     60  int32_t requiredSize;
     61  constexpr auto opcodeSize = sizeof(sandbox::PolicyOpcode);
     62  switch (aSemantics) {
     63    case sandbox::FileSemantics::kAllowAny:
     64      // create, open, query, query_full and rename brokered with 2 opcodes.
     65      requiredSize = (patternRuleSize * 5) + (opcodeSize * 10);
     66      break;
     67    case sandbox::FileSemantics::kAllowReadonly:
     68      // create and open brokered with 4 opcodes
     69      // query and query_full brokered with 2 opcodes
     70      requiredSize = (patternRuleSize * 4) + (opcodeSize * 12);
     71      break;
     72    case sandbox::FileSemantics::kAllowQuery:
     73      // query and query_full brokered with 2 opcodes
     74      requiredSize = (patternRuleSize * 2) + (opcodeSize * 4);
     75      break;
     76    default:
     77      MOZ_CRASH("Unknown FileSemantics");
     78  }
     79 
     80  if (requiredSize > mRemainingSize) {
     81    return sandbox::SBOX_ERROR_NO_SPACE;
     82  }
     83 
     84  mRemainingSize -= requiredSize;
     85  return mConfig->AllowFileAccess(aSemantics, aPattern);
     86 }
     87 
     88 UserFontConfigHelper::UserFontConfigHelper(const wchar_t* aUserFontKeyPath,
     89                                           const nsString& aWinUserProfile,
     90                                           const nsString& aLocalAppData,
     91                                           const nsString& aRoamingAppData)
     92    : mWinUserProfile(aWinUserProfile),
     93      mLocalAppData(aLocalAppData),
     94      mRoamingAppData(aRoamingAppData) {
     95  LSTATUS lStatus =
     96      ::RegOpenKeyExW(HKEY_CURRENT_USER, aUserFontKeyPath, 0,
     97                      KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &mUserFontKey);
     98  if (lStatus != ERROR_SUCCESS) {
     99    // Ensure that mUserFontKey is null on failure.
    100    mUserFontKey = nullptr;
    101  }
    102 }
    103 
    104 UserFontConfigHelper::~UserFontConfigHelper() {
    105  if (mUserFontKey) {
    106    ::RegCloseKey(mUserFontKey);
    107  }
    108 }
    109 
    110 static auto AddRulesForKey(HKEY aFontKey, const nsAString& aWindowsUserFontDir,
    111                           const nsAString& aWinUserProfile,
    112                           SizeTrackingConfig& aConfig,
    113                           Vector<nsString>& aNonUserDirFonts) {
    114  for (DWORD valueIndex = 0; /* break on ERROR_NO_MORE_ITEMS */; ++valueIndex) {
    115    DWORD keyType;
    116    wchar_t name[1024];
    117    wchar_t data[2048];
    118    auto* dataAsBytes = reinterpret_cast<LPBYTE>(data);
    119    DWORD nameLength = std::size(name);
    120    // Pass 1 less wchar_t worth, in case we have to add a null.
    121    DWORD dataSizeInBytes = sizeof(data) - sizeof(wchar_t);
    122    LSTATUS lStatus =
    123        ::RegEnumValueW(aFontKey, valueIndex, name, &nameLength, NULL, &keyType,
    124                        dataAsBytes, &dataSizeInBytes);
    125    if (lStatus == ERROR_NO_MORE_ITEMS) {
    126      break;
    127    }
    128 
    129    // Skip if we failed to retrieve the value.
    130    if (lStatus != ERROR_SUCCESS) {
    131      continue;
    132    }
    133 
    134    // Only strings are used, REG_EXPAND_SZ is not recognized by Fonts panel.
    135    if (keyType != REG_SZ) {
    136      continue;
    137    }
    138 
    139    auto dataSizeInWChars = dataSizeInBytes / sizeof(wchar_t);
    140 
    141    // We test data[dataSizeInWChars - 1] below and might test again after
    142    // decrementing it, so ensure we have at least 2 wchar_ts. A valid font path
    143    // couldn't be this short anyway.
    144    if (dataSizeInWChars < 2) {
    145      continue;
    146    }
    147 
    148    // Size might include a null.
    149    if (data[dataSizeInWChars - 1] == L'\0') {
    150      --dataSizeInWChars;
    151    } else {
    152      // Ensure null terminated.
    153      data[dataSizeInWChars] = L'\0';
    154    }
    155 
    156    // Should be path to font file so reject directories.
    157    if (data[dataSizeInWChars - 1] == L'\\') {
    158      continue;
    159    }
    160 
    161    // If not in the user's dir, store until the end.
    162    if (dataSizeInWChars < aWinUserProfile.Length() ||
    163        !aWinUserProfile.Equals(
    164            nsDependentSubstring(data, aWinUserProfile.Length()),
    165            nsCaseInsensitiveStringComparator)) {
    166      (void)aNonUserDirFonts.emplaceBack(data, dataSizeInWChars);
    167      continue;
    168    }
    169 
    170    // Skip if in windows user font dir.
    171    if (dataSizeInWChars > aWindowsUserFontDir.Length() &&
    172        aWindowsUserFontDir.Equals(
    173            nsDependentSubstring(data, aWindowsUserFontDir.Length()),
    174            nsCaseInsensitiveStringComparator)) {
    175      continue;
    176    }
    177 
    178    auto result =
    179        aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly, data);
    180    if (result != sandbox::SBOX_ALL_OK) {
    181      NS_WARNING("Failed to add specific user font policy rule.");
    182      LOG_W("Failed (ResultCode %d) to add read access to: %S", result, data);
    183      if (result == sandbox::SBOX_ERROR_NO_SPACE) {
    184        return result;
    185      }
    186    }
    187  }
    188 
    189  for (DWORD keyIndex = 0; /* break on ERROR_NO_MORE_ITEMS */; ++keyIndex) {
    190    wchar_t name[1024];
    191    DWORD nameLength = std::size(name);
    192    LSTATUS lStatus = ::RegEnumKeyExW(aFontKey, keyIndex, name, &nameLength,
    193                                      nullptr, nullptr, nullptr, nullptr);
    194    if (lStatus == ERROR_NO_MORE_ITEMS) {
    195      break;
    196    }
    197 
    198    // Skip if we failed to retrieve the key name.
    199    if (lStatus != ERROR_SUCCESS) {
    200      continue;
    201    }
    202 
    203    std::unique_ptr<HKEY, RegCloseKeyDeleter> subKey;
    204    lStatus = ::RegOpenKeyExW(aFontKey, name, 0,
    205                              KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
    206                              getter_Transfers(subKey));
    207    // Skip if we failed to retrieve the key.
    208    if (lStatus != ERROR_SUCCESS) {
    209      continue;
    210    }
    211 
    212    auto result = AddRulesForKey(subKey.get(), aWindowsUserFontDir,
    213                                 aWinUserProfile, aConfig, aNonUserDirFonts);
    214    if (result == sandbox::SBOX_ERROR_NO_SPACE) {
    215      return result;
    216    }
    217  }
    218 
    219  return sandbox::SBOX_ALL_OK;
    220 }
    221 
    222 void UserFontConfigHelper::AddRules(SizeTrackingConfig& aConfig) const {
    223  // Always add rule to allow access to windows user specific fonts dir first.
    224  nsAutoString windowsUserFontDir(mLocalAppData);
    225  windowsUserFontDir += uR"(\Microsoft\Windows\Fonts\*)"_ns;
    226  auto result = aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly,
    227                                        windowsUserFontDir.getW());
    228  if (result != sandbox::SBOX_ALL_OK) {
    229    NS_ERROR("Failed to add Windows user font dir policy rule.");
    230    LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
    231          windowsUserFontDir.getW());
    232  }
    233 
    234  // Add two hard coded rules for Adobe Creative Cloud fonts, as it uses
    235  // AddFontResource to register its fonts so they don't appear in the registry.
    236  nsAutoString adobeLiveTypeFonts(mRoamingAppData);
    237  adobeLiveTypeFonts += uR"(\ADOBE\CORESYNC\PLUGINS\LIVETYPE\R\*)"_ns;
    238  result = aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly,
    239                                   adobeLiveTypeFonts.getW());
    240  if (result != sandbox::SBOX_ALL_OK) {
    241    NS_ERROR("Failed to add Adobe LiveType font dir policy rule.");
    242    LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
    243          adobeLiveTypeFonts.getW());
    244  }
    245  nsAutoString adobeUserOwnedFonts(mRoamingAppData);
    246  adobeUserOwnedFonts += uR"(\ADOBE\USER OWNED FONTS\*)"_ns;
    247  result = aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly,
    248                                   adobeUserOwnedFonts.getW());
    249  if (result != sandbox::SBOX_ALL_OK) {
    250    NS_ERROR("Failed to add Adobe user owned font dir policy rule.");
    251    LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
    252          adobeUserOwnedFonts.getW());
    253  }
    254 
    255  // We failed to open the registry key, we can't do any more.
    256  if (!mUserFontKey) {
    257    return;
    258  }
    259 
    260  // We don't want the wild-card for comparisons.
    261  windowsUserFontDir.SetLength(windowsUserFontDir.Length() - 1);
    262 
    263  // Windows user's profile dir with trailing slash for comparisons.
    264  nsAutoString winUserProfile(mWinUserProfile);
    265  winUserProfile += L'\\';
    266 
    267  // We will add rules for fonts outside of the User's directory at the end, in
    268  // case we run out of space.
    269  Vector<nsString> nonUserDirFonts;
    270 
    271  result = AddRulesForKey(mUserFontKey, windowsUserFontDir, winUserProfile,
    272                          aConfig, nonUserDirFonts);
    273  if (result == sandbox::SBOX_ERROR_NO_SPACE) {
    274    CrashReporter::RecordAnnotationCString(
    275        CrashReporter::Annotation::UserFontRulesExhausted, "inside");
    276    return;
    277  }
    278 
    279  // Finally add rules for fonts outside the user's dir. These are less likely
    280  // to have access blocked by USER_LIMITED.
    281  for (const auto& fontPath : nonUserDirFonts) {
    282    result = aConfig.AllowFileAccess(sandbox::FileSemantics::kAllowReadonly,
    283                                     fontPath.getW());
    284    if (result != sandbox::SBOX_ALL_OK) {
    285      NS_WARNING("Failed to add specific user font policy rule.");
    286      LOG_W("Failed (ResultCode %d) to add read access to: %S", result,
    287            fontPath.getW());
    288      if (result == sandbox::SBOX_ERROR_NO_SPACE) {
    289        CrashReporter::RecordAnnotationCString(
    290            CrashReporter::Annotation::UserFontRulesExhausted, "outside");
    291        return;
    292      }
    293    }
    294  }
    295 }
    296 
    297 }  // namespace sandboxing
    298 
    299 }  // namespace mozilla