TestConfigHelpers.cpp (17602B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "gtest/gtest.h" 8 #include "gmock/gmock.h" 9 10 #include <string> 11 #include <windows.h> 12 13 #include "nsLiteralString.h" 14 #include "nsWindowsHelpers.h" 15 #include "sandbox/win/src/sandbox.h" 16 #include "sandbox/win/src/app_container.h" 17 #include "sandbox/win/src/policy_engine_opcodes.h" 18 #include "../src/sandboxbroker/ConfigHelpers.h" 19 20 using namespace sandbox; 21 using mozilla::sandboxing::UserFontConfigHelper; 22 using ::testing::Eq; 23 using ::testing::Expectation; 24 using ::testing::StartsWith; 25 using ::testing::StrEq; 26 using ::testing::StrictMock; 27 28 // Only allow 2 pages to test by default. 29 constexpr int kDefaultNumberOfPagesForTesting = 2; 30 static const nsLiteralString sWinUserProfile = uR"(C:\Users\Moz User)"_ns; 31 static const nsLiteralString sLocalAppData = 32 uR"(C:\Users\Moz User\AppData\Local)"_ns; 33 static const nsLiteralString sRoamingAppData = 34 uR"(C:\Users\Moz User\AppData\Roaming)"_ns; 35 static const wchar_t* sWinUserFonts = 36 LR"(C:\Users\Moz User\AppData\Local\Microsoft\Windows\Fonts\*)"; 37 static const wchar_t* sAdobeLiveTypeFonts = 38 LR"(C:\Users\Moz User\AppData\Roaming\ADOBE\CORESYNC\PLUGINS\LIVETYPE\R\*)"; 39 static const wchar_t* sAdobeUserOwnedFonts = 40 LR"(C:\Users\Moz User\AppData\Roaming\ADOBE\USER OWNED FONTS\*)"; 41 static const wchar_t* sTestRegKey = LR"(Software\MozFontsPathsTest)"; 42 static const wchar_t* sTestFailRegKey = LR"(Software\MozFontsPathsTestFail)"; 43 44 namespace mozilla { 45 46 class MockConfig : public TargetConfig { 47 public: 48 MOCK_METHOD(ResultCode, AllowFileAccess, 49 (FileSemantics semantics, const wchar_t* pattern), (override)); 50 51 // Remaining methods should not be called during tests. 52 MOCK_METHOD(bool, IsConfigured, (), (const, override)); 53 MOCK_METHOD(ResultCode, SetTokenLevel, 54 (TokenLevel initial, TokenLevel lockdown), (override)); 55 MOCK_METHOD(TokenLevel, GetInitialTokenLevel, (), (const, override)); 56 MOCK_METHOD(TokenLevel, GetLockdownTokenLevel, (), (const, override)); 57 MOCK_METHOD(void, SetDoNotUseRestrictingSIDs, (), (override)); 58 MOCK_METHOD(bool, GetUseRestrictingSIDs, (), (override)); 59 MOCK_METHOD(void, SetForceKnownDllLoadingFallback, (), (override)); 60 MOCK_METHOD(ResultCode, SetJobLevel, 61 (JobLevel job_level, uint32_t ui_exceptions), (override)); 62 MOCK_METHOD(JobLevel, GetJobLevel, (), (const, override)); 63 MOCK_METHOD(void, SetJobMemoryLimit, (size_t memory_limit), (override)); 64 MOCK_METHOD(ResultCode, AllowNamedPipes, (const wchar_t* pattern), 65 (override)); 66 MOCK_METHOD(ResultCode, AllowRegistryRead, (const wchar_t* pattern), 67 (override)); 68 MOCK_METHOD(ResultCode, AllowExtraDlls, (const wchar_t* pattern), (override)); 69 MOCK_METHOD(ResultCode, SetFakeGdiInit, (), (override)); 70 MOCK_METHOD(ResultCode, AllowLineBreaking, (), (override)); 71 MOCK_METHOD(void, AddDllToUnload, (const wchar_t* dll_name), (override)); 72 MOCK_METHOD(ResultCode, SetIntegrityLevel, (IntegrityLevel level), 73 (override)); 74 MOCK_METHOD(IntegrityLevel, GetIntegrityLevel, (), (const, override)); 75 MOCK_METHOD(void, SetDelayedIntegrityLevel, (IntegrityLevel level), 76 (override)); 77 MOCK_METHOD(ResultCode, SetLowBox, (const wchar_t* sid), (override)); 78 MOCK_METHOD(ResultCode, SetProcessMitigations, (MitigationFlags flags), 79 (override)); 80 MOCK_METHOD(MitigationFlags, GetProcessMitigations, (), (override)); 81 MOCK_METHOD(ResultCode, SetDelayedProcessMitigations, (MitigationFlags flags), 82 (override)); 83 MOCK_METHOD(MitigationFlags, GetDelayedProcessMitigations, (), 84 (const, override)); 85 MOCK_METHOD(void, AddRestrictingRandomSid, (), (override)); 86 MOCK_METHOD(void, SetLockdownDefaultDacl, (), (override)); 87 MOCK_METHOD(ResultCode, AddAppContainerProfile, 88 (const wchar_t* package_name, bool create_profile), (override)); 89 MOCK_METHOD(scoped_refptr<AppContainer>, GetAppContainer, (), (override)); 90 MOCK_METHOD(ResultCode, AddKernelObjectToClose, 91 (const wchar_t* handle_type, const wchar_t* handle_name), 92 (override)); 93 MOCK_METHOD(ResultCode, SetDisconnectCsrss, (), (override)); 94 MOCK_METHOD(void, SetDesktop, (Desktop desktop), (override)); 95 MOCK_METHOD(void, SetFilterEnvironment, (bool filter), (override)); 96 MOCK_METHOD(bool, GetEnvironmentFiltered, (), (override)); 97 MOCK_METHOD(void, SetZeroAppShim, (), (override)); 98 }; 99 100 #define EXPECT_READONLY_EQ(aRulePath) \ 101 EXPECT_CALL(mConfig, AllowFileAccess(Eq(FileSemantics::kAllowReadonly), \ 102 StrEq(aRulePath))) 103 104 #define EXPECT_READONLY_STARTS(aRulePath) \ 105 EXPECT_CALL(mConfig, AllowFileAccess(Eq(FileSemantics::kAllowReadonly), \ 106 StartsWith(aRulePath))) 107 108 static void SetUpPathsInKey(HKEY aKey, 109 const std::vector<std::wstring_view>& aFontPaths) { 110 for (size_t i = 0; i < aFontPaths.size(); ++i) { 111 const auto* pathBytes = reinterpret_cast<const BYTE*>(aFontPaths[i].data()); 112 size_t sizeInBytes = (aFontPaths[i].length() + 1) * sizeof(wchar_t); 113 ::RegSetValueExW(aKey, std::to_wstring(i).c_str(), 0, REG_SZ, pathBytes, 114 sizeInBytes); 115 } 116 } 117 118 class UserFontConfigHelperTest : public testing::Test { 119 protected: 120 // We always expect the Windows User font dir rule to be added. 121 UserFontConfigHelperTest() 122 : mWinUserFontCall(EXPECT_READONLY_EQ(sWinUserFonts)) { 123 EXPECT_READONLY_EQ(sAdobeLiveTypeFonts); 124 EXPECT_READONLY_EQ(sAdobeUserOwnedFonts); 125 ::RegCreateKeyExW(HKEY_CURRENT_USER, sTestRegKey, 0, nullptr, 126 REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, 127 &mTestUserFontKey, nullptr); 128 } 129 130 ~UserFontConfigHelperTest() { 131 if (mTestUserFontKey) { 132 ::RegCloseKey(mTestUserFontKey); 133 } 134 ::RegDeleteTreeW(HKEY_CURRENT_USER, sTestRegKey); 135 } 136 137 void SetUpPaths(const std::vector<std::wstring_view>& aFontPaths) { 138 SetUpPathsInKey(mTestUserFontKey, aFontPaths); 139 } 140 141 void CreateHelperAndCallAddRules() { 142 UserFontConfigHelper policyHelper(sTestRegKey, sWinUserProfile, 143 sLocalAppData, sRoamingAppData); 144 sandboxing::SizeTrackingConfig trackingPolicy(&mConfig, 145 mNumberOfStoragePages); 146 policyHelper.AddRules(trackingPolicy); 147 } 148 149 // StrictMock because we only expect AllowFileAccess to be called. 150 StrictMock<MockConfig> mConfig; 151 const Expectation mWinUserFontCall; 152 HKEY mTestUserFontKey = nullptr; 153 int32_t mNumberOfStoragePages = kDefaultNumberOfPagesForTesting; 154 }; 155 156 TEST_F(UserFontConfigHelperTest, WindowsDirRuleAddedOnKeyFailure) { 157 // Create helper with incorrect key name. 158 UserFontConfigHelper policyHelper(sTestFailRegKey, sWinUserProfile, 159 sLocalAppData, sRoamingAppData); 160 sandboxing::SizeTrackingConfig trackingPolicy(&mConfig, 1); 161 policyHelper.AddRules(trackingPolicy); 162 } 163 164 TEST_F(UserFontConfigHelperTest, PathsInsideUsersDirAdded) { 165 SetUpPaths({LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)"}); 166 167 // We expect the windows user font rule to be added first. 168 EXPECT_READONLY_EQ(LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)") 169 .After(mWinUserFontCall); 170 171 CreateHelperAndCallAddRules(); 172 } 173 174 TEST_F(UserFontConfigHelperTest, PathsInsideUsersDirAddedIgnoringCase) { 175 SetUpPaths({LR"(C:\users\moz uSER\Fonts\FontFile1.ttf)"}); 176 177 EXPECT_READONLY_EQ(LR"(C:\users\moz uSER\Fonts\FontFile1.ttf)") 178 .After(mWinUserFontCall); 179 180 CreateHelperAndCallAddRules(); 181 } 182 183 TEST_F(UserFontConfigHelperTest, PathsOutsideUsersDirAdded) { 184 SetUpPaths({LR"(C:\ProgramData\Fonts\FontFile1.ttf)", 185 LR"(C:\programdata\Fonts\FontFile2.ttf)"}); 186 187 EXPECT_READONLY_EQ(LR"(C:\ProgramData\Fonts\FontFile1.ttf)") 188 .After(mWinUserFontCall); 189 EXPECT_READONLY_EQ(LR"(C:\programdata\Fonts\FontFile2.ttf)") 190 .After(mWinUserFontCall); 191 192 CreateHelperAndCallAddRules(); 193 } 194 195 TEST_F(UserFontConfigHelperTest, SubKeyPathsInsideUsersDirAdded) { 196 SetUpPaths({LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)"}); 197 std::unique_ptr<HKEY, RegCloseKeyDeleter> subKey; 198 auto lStatus = ::RegCreateKeyExW(mTestUserFontKey, L"SubKey", 0, nullptr, 199 REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, 200 getter_Transfers(subKey), nullptr); 201 ASSERT_EQ(lStatus, ERROR_SUCCESS); 202 SetUpPathsInKey(subKey.get(), {LR"(C:\Users\Moz User\Fonts\FontFile2.ttf)"}); 203 204 // We expect the windows user font rule to be added first. 205 auto& fontFile1 = 206 EXPECT_READONLY_EQ(LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)") 207 .After(mWinUserFontCall); 208 EXPECT_READONLY_EQ(LR"(C:\Users\Moz User\Fonts\FontFile2.ttf)") 209 .After(fontFile1); 210 211 CreateHelperAndCallAddRules(); 212 } 213 214 TEST_F(UserFontConfigHelperTest, PathsOutsideUsersDirAddedAtEnd) { 215 // We set up the paths in a particular order, but this doesn't guarantee the 216 // order returned from registry calls. However the rule adding code should 217 // guarantee that non-user dir fonts are added at the end. 218 const auto* userFont1 = LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)"; 219 const auto* userFont2 = LR"(C:\Users\Moz User\Fonts\FontFile2.ttf)"; 220 const auto* userFont3 = LR"(C:\Users\Moz User\Fonts\FontFile3.ttf)"; 221 const auto* pdFont1 = LR"(C:\ProgramData\Fonts\FontFile1.ttf)"; 222 const auto* pdFont2 = LR"(C:\ProgramData\Fonts\FontFile2.ttf)"; 223 SetUpPaths({pdFont1, userFont1, pdFont2, userFont2, userFont3}); 224 225 auto& userDirFont1 = EXPECT_READONLY_EQ(userFont1).After(mWinUserFontCall); 226 auto& userDirFont2 = EXPECT_READONLY_EQ(userFont2).After(mWinUserFontCall); 227 auto& userDirFont3 = EXPECT_READONLY_EQ(userFont3).After(mWinUserFontCall); 228 EXPECT_READONLY_EQ(pdFont1).After(userDirFont1, userDirFont2, userDirFont3); 229 EXPECT_READONLY_EQ(pdFont2).After(userDirFont1, userDirFont2, userDirFont3); 230 231 CreateHelperAndCallAddRules(); 232 } 233 234 TEST_F(UserFontConfigHelperTest, SubKeyPathsOutsideUsersDirAddedAtEnd) { 235 // We set up the paths in a particular order, but this doesn't guarantee the 236 // order returned from registry calls. However the rule adding code should 237 // guarantee that non-user dir fonts are added at the end. 238 const auto* userFont1 = LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)"; 239 const auto* userFont2 = LR"(C:\Users\Moz User\Fonts\FontFile2.ttf)"; 240 const auto* userFont3 = LR"(C:\Users\Moz User\Fonts\FontFile3.ttf)"; 241 const auto* pdFont1 = LR"(C:\ProgramData\Fonts\FontFile1.ttf)"; 242 const auto* pdFont2 = LR"(C:\ProgramData\Fonts\FontFile2.ttf)"; 243 SetUpPaths({pdFont1, userFont1, userFont2}); 244 std::unique_ptr<HKEY, RegCloseKeyDeleter> subKey; 245 auto lStatus = ::RegCreateKeyExW(mTestUserFontKey, L"SubKey", 0, nullptr, 246 REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, 247 getter_Transfers(subKey), nullptr); 248 ASSERT_EQ(lStatus, ERROR_SUCCESS); 249 SetUpPathsInKey(subKey.get(), {pdFont2, userFont3}); 250 251 auto& userDirFont1 = EXPECT_READONLY_EQ(userFont1).After(mWinUserFontCall); 252 auto& userDirFont2 = EXPECT_READONLY_EQ(userFont2).After(mWinUserFontCall); 253 auto& userDirFont3 = 254 EXPECT_READONLY_EQ(userFont3).After(userDirFont1, userDirFont2); 255 EXPECT_READONLY_EQ(pdFont1).After(userDirFont3); 256 EXPECT_READONLY_EQ(pdFont2).After(userDirFont3); 257 258 CreateHelperAndCallAddRules(); 259 } 260 261 TEST_F(UserFontConfigHelperTest, NonStringValueIsIgnored) { 262 DWORD regValue = 42; 263 ::RegSetValueExW(mTestUserFontKey, L"Liff", 0, REG_DWORD, 264 reinterpret_cast<const BYTE*>(®Value), sizeof(regValue)); 265 wchar_t multiPath[] = LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)"; 266 ::RegSetValueExW(mTestUserFontKey, L"MultiStr", 0, REG_MULTI_SZ, 267 reinterpret_cast<const BYTE*>(multiPath), sizeof(multiPath)); 268 ::RegSetValueExW(mTestUserFontKey, L"ExpandStr", 0, REG_EXPAND_SZ, 269 reinterpret_cast<const BYTE*>(multiPath), sizeof(multiPath)); 270 271 EXPECT_READONLY_EQ(LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)").Times(0); 272 273 CreateHelperAndCallAddRules(); 274 } 275 276 TEST_F(UserFontConfigHelperTest, PathNotNullTerminated) { 277 // If you just miss the null in the size it stills gets stored with it, so you 278 // have to make the next character non-null. 279 wchar_t fontPath[] = LR"(C:\Users\Moz User\Fonts\FontFile1.ttfx)"; 280 ::RegSetValueExW(mTestUserFontKey, L"NoNull", 0, REG_SZ, 281 reinterpret_cast<const BYTE*>(fontPath), 282 sizeof(fontPath) - (2 * sizeof(wchar_t))); 283 284 EXPECT_READONLY_EQ(LR"(C:\Users\Moz User\Fonts\FontFile1.ttf)") 285 .After(mWinUserFontCall); 286 287 CreateHelperAndCallAddRules(); 288 } 289 290 TEST_F(UserFontConfigHelperTest, PathEmpty) { 291 wchar_t fontPath[] = L""; 292 ::RegSetValueExW(mTestUserFontKey, L"EmptyNoNull", 0, REG_SZ, 293 reinterpret_cast<const BYTE*>(fontPath), sizeof(fontPath)); 294 295 EXPECT_READONLY_EQ(fontPath).Times(0); 296 297 CreateHelperAndCallAddRules(); 298 } 299 300 TEST_F(UserFontConfigHelperTest, PathEmptyNotNullTerminated) { 301 // If you just miss the null in the size it stills gets stored with it, so you 302 // have to make the next character non-null. 303 wchar_t fontPath[] = L"F"; 304 ::RegSetValueExW(mTestUserFontKey, L"EmptyNoNull", 0, REG_SZ, 305 reinterpret_cast<const BYTE*>(fontPath), 0); 306 307 EXPECT_READONLY_EQ(L"").Times(0); 308 309 CreateHelperAndCallAddRules(); 310 } 311 312 TEST_F(UserFontConfigHelperTest, DirsAreIgnored) { 313 SetUpPaths({LR"(C:\Users\Moz User\Fonts\)"}); 314 315 EXPECT_READONLY_EQ(LR"(C:\Users\Moz Us]er\Fonts\)").Times(0); 316 317 CreateHelperAndCallAddRules(); 318 } 319 320 TEST_F(UserFontConfigHelperTest, PathsInWindowsUsersFontDirNotAdded) { 321 SetUpPaths({ 322 LR"(C:\Users\Moz User\AppData\Local\Microsoft\Windows\Fonts\FontFile1.ttf)", 323 LR"(C:\Users\Moz User\AppData\Local\Microsoft\Windows\Fonts\Sub\FontFile2.ttf)", 324 }); 325 326 EXPECT_READONLY_EQ( 327 LR"(C:\Users\Moz User\AppData\Local\Microsoft\Windows\Fonts\FontFile1.ttf)") 328 .Times(0); 329 EXPECT_READONLY_EQ( 330 LR"(C:\Users\Moz User\AppData\Local\Microsoft\Windows\Fonts\Sub\FontFile2.ttf)") 331 .Times(0); 332 333 CreateHelperAndCallAddRules(); 334 } 335 336 TEST_F(UserFontConfigHelperTest, 337 PathsInWindowsUsersFontDirNotAddedIgnoringCase) { 338 SetUpPaths({ 339 LR"(c:\Users\mOZ user\aPPdATA\Local\microsoft\wINDows\Fonts\FontFile1.ttf)", 340 LR"(c:\uSERS\moz user\aPPdATA\lOCAL\MICRosoft\WindOWS\fONTS\Sub\FontFile2.ttf)", 341 }); 342 343 EXPECT_READONLY_EQ( 344 LR"(c:\Users\mOZ user\aPPdATA\Local\microsoft\wINDows\Fonts\FontFile1.ttf)") 345 .Times(0); 346 EXPECT_READONLY_EQ( 347 LR"(c:\uSERS\moz user\aPPdATA\lOCAL\MICRosoft\WindOWS\fONTS\Sub\FontFile2.ttf)") 348 .Times(0); 349 350 CreateHelperAndCallAddRules(); 351 } 352 353 auto RuleSize(const wchar_t* aRulePath) { 354 return (12 * sizeof(sandbox::PolicyOpcode)) + 355 ((wcslen(aRulePath) + 4) * sizeof(wchar_t) * 4); 356 } 357 358 std::wstring MakeLongFontPath(const wchar_t* aPrefix, const wchar_t* aSuffix) { 359 static size_t sReqPathLen = []() { 360 // Take the bytes required for the static rules from the starting memory 361 // allowance for tests. 362 size_t remainingSpace = 363 (4096 * kDefaultNumberOfPagesForTesting) - RuleSize(sWinUserFonts) - 364 RuleSize(sAdobeLiveTypeFonts) - RuleSize(sAdobeUserOwnedFonts); 365 366 // We want 3 paths to be too big, so divide by 3 and reverse the formula. 367 size_t spacePerFontPath = remainingSpace / 3; 368 size_t reqFontLen = 369 ((spacePerFontPath - (12 * sizeof(sandbox::PolicyOpcode))) / 370 (4 * sizeof(wchar_t))) - 371 4; 372 373 // Add on 1 to make it too long for 3 paths. 374 return reqFontLen + 1; 375 }(); 376 377 std::wstring longFontPath(aPrefix); 378 longFontPath.append(sReqPathLen - longFontPath.length() - wcslen(aSuffix), 379 'F'); 380 longFontPath.append(aSuffix); 381 return longFontPath; 382 } 383 384 TEST_F(UserFontConfigHelperTest, PathsTooLongForStorage) { 385 // These font paths take up enough storage such that, with the Windows user 386 // font dir rule, only two will fit in the available 4K of storage. Note that 387 // we can't guarantee the order they are returned from the registry. 388 auto path1 = MakeLongFontPath(LR"(C:\Users\Moz User\)", L"1"); 389 auto path2 = MakeLongFontPath(LR"(C:\Users\Moz User\)", L"2"); 390 auto path3 = MakeLongFontPath(LR"(C:\Users\Moz User\)", L"3"); 391 SetUpPaths({ 392 path1, 393 path2, 394 path3, 395 }); 396 397 path1.pop_back(); 398 EXPECT_READONLY_STARTS(path1).Times(2).After(mWinUserFontCall); 399 400 CreateHelperAndCallAddRules(); 401 } 402 403 TEST_F(UserFontConfigHelperTest, PathsTooLongOneOutsideUserProfile) { 404 // These font paths take up enough storage such that, with the Windows user 405 // font dir rule, only two will fit in the available 4K of storage. 406 // However one is outside the user profile, so we can be certain about which 407 // rules should be added. 408 auto path1 = MakeLongFontPath(LR"(C:\ProgramData\)", L"1"); 409 auto path2 = MakeLongFontPath(LR"(C:\Users\Moz User\)", L"2"); 410 auto path3 = MakeLongFontPath(LR"(C:\Users\Moz User\)", L"3"); 411 SetUpPaths({ 412 path1, 413 path2, 414 path3, 415 }); 416 417 EXPECT_READONLY_EQ(path1).Times(0); 418 EXPECT_READONLY_EQ(path2).After(mWinUserFontCall); 419 EXPECT_READONLY_EQ(path3).After(mWinUserFontCall); 420 421 CreateHelperAndCallAddRules(); 422 } 423 424 } // namespace mozilla