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