forked from mirrors/gecko-dev
		
	 028ad1b883
			
		
	
	
		028ad1b883
		
	
	
	
	
		
			
			browser/components/shell/WindowsUserChoice.cpp(233,23): error: comparison of integers of different signs: 'int' and 'const size_t' (aka 'const unsigned long long') [-Werror,-Wsign-compare]
    for (int j = 0; j < DWORDS_PER_BLOCK; ++j) {
                    ~ ^ ~~~~~~~~~~~~~~~~
browser/components/shell/WindowsUserChoice.cpp(388,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < ArrayLength(exts); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~
browser/components/shell/nsWindowsShellService.cpp(1225,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < ArrayLength(shortcutCSIDLs); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
browser/components/shell/nsWindowsShellService.cpp(1492,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < ArrayLength(folders); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~
dom/media/platforms/wmf/MFTDecoder.cpp(85,23): error: comparison of integers of different signs: 'int' and 'UINT32' (aka 'unsigned int') [-Werror,-Wsign-compare]
    for (int i = 1; i < actsNum; i++) {
                    ~ ^ ~~~~~~~
gfx/2d/Factory.cpp(1276,21): error: comparison of integers of different signs: 'int' and 'uint32_t' (aka 'unsigned int') [-Werror,-Wsign-compare]
  for (int y = 0; y < height; y++) {
                  ~ ^ ~~~~~~
gfx/layers/d3d11/CompositorD3D11.cpp(1096,36): error: comparison of integers of different signs: 'UINT' (aka 'unsigned int') and 'int' [-Werror,-Wsign-compare]
        swapDesc.BufferDesc.Height == mSize.height) ||
        ~~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~
gfx/layers/d3d11/CompositorD3D11.cpp(1095,35): error: comparison of integers of different signs: 'UINT' (aka 'unsigned int') and 'int' [-Werror,-Wsign-compare]
  if (((swapDesc.BufferDesc.Width == mSize.width &&
        ~~~~~~~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~
gfx/layers/d3d11/TextureD3D11.cpp(1278,30): error: comparison of integers of different signs: 'UINT' (aka 'unsigned int') and 'int' [-Werror,-Wsign-compare]
          currentDesc.Height != mSize.height ||
          ~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~
gfx/layers/d3d11/TextureD3D11.cpp(1277,29): error: comparison of integers of different signs: 'UINT' (aka 'unsigned int') and 'int' [-Werror,-Wsign-compare]
      if (currentDesc.Width != mSize.width ||
          ~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~
gfx/layers/ipc/ContentCompositorBridgeParent.cpp(248,19): error: comparison of integers of different signs: 'const uint32_t' (aka 'const unsigned int') and 'int32_t' (aka 'int') [-Werror,-Wsign-compare]
  if (sequenceNum == status.sequenceNumber() && !dm->HasDeviceReset()) {
      ~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~
gfx/thebes/D3D11Checks.cpp(129,21): error: comparison of integers of different signs: 'int' and 'unsigned int' [-Werror,-Wsign-compare]
    if (resultColor != 0xffffff00) {
        ~~~~~~~~~~~ ^  ~~~~~~~~~~
gfx/thebes/D3D11Checks.cpp(154,23): error: comparison of integers of different signs: 'int' and 'unsigned long long' [-Werror,-Wsign-compare]
    for (int i = 0; i < PR_ARRAY_SIZE(checkModules); i += 1) {
                    ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
gfx/thebes/D3D11Checks.cpp(409,14): error: comparison of integers of different signs: 'int32_t' (aka 'int') and 'UINT' (aka 'unsigned int') [-Werror,-Wsign-compare]
  if (vendor != desc.VendorId) {
      ~~~~~~ ^  ~~~~~~~~~~~~~
gfx/thebes/gfxDWriteFontList.cpp(1248,39): error: comparison of integers of different signs: 'unsigned int' and 'int' [-Werror,-Wsign-compare]
        addFamily(names[index], index != sysLocIndex);
                                ~~~~~ ^  ~~~~~~~~~~~
intl/lwbrk/nsUniscribeBreaker.cpp(121,21): error: comparison of integers of different signs: 'int' and 'uint32_t' (aka 'unsigned int') [-Werror,-Wsign-compare]
  for (int i = 0; i < aLength; ++i) {
                  ~ ^ ~~~~~~~
intl/lwbrk/nsUniscribeBreaker.cpp(132,23): error: comparison of integers of different signs: 'int' and 'uint32_t' (aka 'unsigned int') [-Werror,-Wsign-compare]
    for (int i = 0; i < aLength; ++i) {
                    ~ ^ ~~~~~~~
intl/lwbrk/nsUniscribeBreaker.cpp(138,23): error: comparison of integers of different signs: 'int' and 'uint32_t' (aka 'unsigned int') [-Werror,-Wsign-compare]
    for (int i = 0; i < aLength; ++i) {
                    ~ ^ ~~~~~~~
mozglue/misc/PreXULSkeletonUI.cpp(319,26): error: comparison of integers of different signs: 'std::basic_string<char>::size_type' (aka 'unsigned long long') and 'int' [-Werror,-Wsign-compare]
    while (line.length() > whitespace &&
           ~~~~~~~~~~~~~ ^ ~~~~~~~~~~
mozglue/misc/PreXULSkeletonUI.cpp(1003,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 1; i < noPlaceholderSpans.length(); i++) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
mozglue/misc/PreXULSkeletonUI.cpp(1708,21): error: comparison of integers of different signs: 'int' and 'unsigned long long' [-Werror,-Wsign-compare]
  for (int i = 0; i < dataLen / (2 * sizeof(double)); i++) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
security/sandbox/chromium-shim/sandbox/win/permissionsService.cpp(40,16): error: comparison of integers of different signs: 'int' and 'const std::basic_string<wchar_t>::size_type' (aka 'const unsigned long long') [-Werror,-Wsign-compare]
  if (slashIdx != std::wstring::npos) {
      ~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~
toolkit/components/aboutthirdparty/tests/gtest/TestAboutThirdParty.cpp(107,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < ArrayLength(kDirectoriesUnsorted); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/crashreporter/breakpad-client/windows/crash_generation/crash_generation_server.cc(957,23): error: comparison of integers of different signs: 'int' and 'const size_t' (aka 'const unsigned long long') [-Werror,-Wsign-compare]
    for (int i = 0; i < kExceptionAppMemoryRegions; i++) {
                    ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/crashreporter/client/crashreporter_win.cpp(373,21): error: comparison of integers of different signs: 'int' and 'unsigned long long' [-Werror,-Wsign-compare]
  for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/crashreporter/client/crashreporter_win.cpp(671,21): error: comparison of integers of different signs: 'int' and 'unsigned long long' [-Werror,-Wsign-compare]
  for (int i = 0; i < sizeof(controls) / sizeof(controls[0]); i++) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/crashreporter/client/crashreporter_win.cpp(1048,21): error: comparison of integers of different signs: 'int' and 'unsigned long long' [-Werror,-Wsign-compare]
  for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp(248,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < mozilla::ArrayLength(associations); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/system/windowsproxy/ProxyUtils.cpp(27,36): error: comparison of integers of different signs: 'const int' and 'nsTArray_base::size_type' (aka 'unsigned long long') [-Werror,-Wsign-compare]
                             if (i < addr.Length()) {
                                 ~ ^ ~~~~~~~~~~~~~
toolkit/xre/dllservices/mozglue/interceptor/Arm64.h(178,28): error: comparison of integers of different signs: 'int32_t' (aka 'int') and 'unsigned int' [-Werror,-Wsign-compare]
  if (signbits && signbits != 0xFE000000) {
                  ~~~~~~~~ ^  ~~~~~~~~~~
obj-build/dist/include/gtest/gtest.h(1842,54): note: expanded from macro 'EXPECT_EQ'
  EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
                                                     ^
obj-build/dist/include/gtest/gtest.h(1354,11): error: comparison of integers of different signs: 'const unsigned int' and 'const int' [-Werror,-Wsign-compare]
  if (lhs == rhs) {
      ~~~ ^  ~~~
obj-build/dist/include/gtest/gtest.h(1373,12): note: in instantiation of function template specialization 'testing::internal::CmpHelperEQ<unsigned int, int>' requested here
    return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
           ^
toolkit/xre/dllservices/tests/gtest/TestUntrustedModules.cpp(35,5): note: in instantiation of function template specialization 'testing::internal::EqHelper::Compare<unsigned int, int, nullptr>' requested here
    EXPECT_EQ(mCounters.Count(), N);
    ^
obj-build/dist/include/gtest/gtest.h(1842,54): note: expanded from macro 'EXPECT_EQ'
  EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
                                                     ^
toolkit/xre/dllservices/tests/gtest/TestUntrustedModules.cpp(210,28): note: in instantiation of function template specialization 'ModuleLoadCounter::Remains<1>' requested here
    EXPECT_TRUE(waitForOne.Remains({kTestModules[0]}, {0}));
                           ^
toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp(139,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp(151,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp(164,21): error: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long long') [-Werror,-Wsign-compare]
  for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
obj-build/dist/include/gtest/gtest.h(1354,11): error: comparison of integers of different signs: 'const int' and 'const unsigned long long' [-Werror,-Wsign-compare]
  if (lhs == rhs) {
      ~~~ ^  ~~~
obj-build/dist/include/gtest/gtest.h(1373,12): note: in instantiation of function template specialization 'testing::internal::CmpHelperEQ<int, unsigned long long>' requested here
    return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
           ^
toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp(138,3): note: in instantiation of function template specialization 'testing::internal::EqHelper::Compare<int, unsigned long long, nullptr>' requested here
  EXPECT_EQ(len, ArrayLength(kExpectedArgsW));
  ^
widget/windows/TSFTextStore.cpp(3455,28): error: comparison of integers of different signs: 'uint32_t' (aka 'unsigned int') and 'long' [-Werror,-Wsign-compare]
          range.mEndOffset == end - mComposition->StartOffset() &&
          ~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
widget/windows/TSFTextStore.cpp(3454,30): error: comparison of integers of different signs: 'uint32_t' (aka 'unsigned int') and 'long' [-Werror,-Wsign-compare]
      if (range.mStartOffset == start - mComposition->StartOffset() &&
          ~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
xpfe/appshell/AppWindow.cpp(1900,21): error: comparison of integers of different signs: 'int' and 'uint32_t' (aka 'unsigned int') [-Werror,-Wsign-compare]
  for (int i = 0; i < toolbarSprings->Length(); i++) {
                  ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~
Differential Revision: https://phabricator.services.mozilla.com/D144695
		
	
			
		
			
				
	
	
		
			422 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			422 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* 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
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /*
 | |
|  * Generate and check the UserChoice Hash, which protects file and protocol
 | |
|  * associations on Windows 10.
 | |
|  *
 | |
|  * NOTE: This is also used in the WDBA, so it avoids XUL and XPCOM.
 | |
|  *
 | |
|  * References:
 | |
|  * - PS-SFTA by Danysys <https://github.com/DanysysTeam/PS-SFTA>
 | |
|  *  - based on a PureBasic version by LMongrain
 | |
|  *    <https://github.com/DanysysTeam/SFTA>
 | |
|  * - AssocHashGen by "halfmeasuresdisabled", see bug 1225660 and
 | |
|  *   <https://www.reddit.com/r/ReverseEngineering/comments/3t7q9m/assochashgen_a_reverse_engineered_version_of/>
 | |
|  * - SetUserFTA changelog
 | |
|  *   <https://kolbi.cz/blog/2017/10/25/setuserfta-userchoice-hash-defeated-set-file-type-associations-per-user/>
 | |
|  */
 | |
| 
 | |
| #include <windows.h>
 | |
| #include <sddl.h>      // for ConvertSidToStringSidW
 | |
| #include <wincrypt.h>  // for CryptoAPI base64
 | |
| #include <bcrypt.h>    // for CNG MD5
 | |
| #include <winternl.h>  // for NT_SUCCESS()
 | |
| 
 | |
| #include "mozilla/ArrayUtils.h"
 | |
| #include "mozilla/UniquePtr.h"
 | |
| #include "nsWindowsHelpers.h"
 | |
| 
 | |
| #include "WindowsUserChoice.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| 
 | |
| UniquePtr<wchar_t[]> GetCurrentUserStringSid() {
 | |
|   HANDLE rawProcessToken;
 | |
|   if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY,
 | |
|                           &rawProcessToken)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   nsAutoHandle processToken(rawProcessToken);
 | |
| 
 | |
|   DWORD userSize = 0;
 | |
|   if (!(!::GetTokenInformation(processToken.get(), TokenUser, nullptr, 0,
 | |
|                                &userSize) &&
 | |
|         GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   auto userBytes = MakeUnique<unsigned char[]>(userSize);
 | |
|   if (!::GetTokenInformation(processToken.get(), TokenUser, userBytes.get(),
 | |
|                              userSize, &userSize)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   wchar_t* rawSid = nullptr;
 | |
|   if (!::ConvertSidToStringSidW(
 | |
|           reinterpret_cast<PTOKEN_USER>(userBytes.get())->User.Sid, &rawSid)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   UniquePtr<wchar_t, LocalFreeDeleter> sid(rawSid);
 | |
| 
 | |
|   // Copy instead of passing UniquePtr<wchar_t, LocalFreeDeleter> back to
 | |
|   // the caller.
 | |
|   int sidLen = ::lstrlenW(sid.get()) + 1;
 | |
|   auto outSid = MakeUnique<wchar_t[]>(sidLen);
 | |
|   memcpy(outSid.get(), sid.get(), sidLen * sizeof(wchar_t));
 | |
| 
 | |
|   return outSid;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Create the string which becomes the input to the UserChoice hash.
 | |
|  *
 | |
|  * @see GenerateUserChoiceHash() for parameters.
 | |
|  *
 | |
|  * @return The formatted string, nullptr on failure.
 | |
|  *
 | |
|  * NOTE: This uses the format as of Windows 10 20H2 (latest as of this writing),
 | |
|  * used at least since 1803.
 | |
|  * There was at least one older version, not currently supported: On Win10 RTM
 | |
|  * (build 10240, aka 1507) the hash function is the same, but the timestamp and
 | |
|  * User Experience string aren't included; instead (for protocols) the string
 | |
|  * ends with the exe path. The changelog of SetUserFTA suggests the algorithm
 | |
|  * changed in 1703, so there may be two versions: before 1703, and 1703 to now.
 | |
|  */
 | |
| static UniquePtr<wchar_t[]> FormatUserChoiceString(const wchar_t* aExt,
 | |
|                                                    const wchar_t* aUserSid,
 | |
|                                                    const wchar_t* aProgId,
 | |
|                                                    SYSTEMTIME aTimestamp) {
 | |
|   aTimestamp.wSecond = 0;
 | |
|   aTimestamp.wMilliseconds = 0;
 | |
| 
 | |
|   FILETIME fileTime = {0};
 | |
|   if (!::SystemTimeToFileTime(&aTimestamp, &fileTime)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // This string is built into Windows as part of the UserChoice hash algorithm.
 | |
|   // It might vary across Windows SKUs (e.g. Windows 10 vs. Windows Server), or
 | |
|   // across builds of the same SKU, but this is the only currently known
 | |
|   // version. There isn't any known way of deriving it, so we assume this
 | |
|   // constant value. If we are wrong, we will not be able to generate correct
 | |
|   // UserChoice hashes.
 | |
|   const wchar_t* userExperience =
 | |
|       L"User Choice set via Windows User Experience "
 | |
|       L"{D18B6DD5-6124-4341-9318-804003BAFA0B}";
 | |
| 
 | |
|   const wchar_t* userChoiceFmt =
 | |
|       L"%s%s%s"
 | |
|       L"%08lx"
 | |
|       L"%08lx"
 | |
|       L"%s";
 | |
|   int userChoiceLen = _scwprintf(userChoiceFmt, aExt, aUserSid, aProgId,
 | |
|                                  fileTime.dwHighDateTime,
 | |
|                                  fileTime.dwLowDateTime, userExperience);
 | |
|   userChoiceLen += 1;  // _scwprintf does not include the terminator
 | |
| 
 | |
|   auto userChoice = MakeUnique<wchar_t[]>(userChoiceLen);
 | |
|   _snwprintf_s(userChoice.get(), userChoiceLen, _TRUNCATE, userChoiceFmt, aExt,
 | |
|                aUserSid, aProgId, fileTime.dwHighDateTime,
 | |
|                fileTime.dwLowDateTime, userExperience);
 | |
| 
 | |
|   ::CharLowerW(userChoice.get());
 | |
| 
 | |
|   return userChoice;
 | |
| }
 | |
| 
 | |
| // @return The MD5 hash of the input, nullptr on failure.
 | |
| static UniquePtr<DWORD[]> CNG_MD5(const unsigned char* bytes, ULONG bytesLen) {
 | |
|   constexpr ULONG MD5_BYTES = 16;
 | |
|   constexpr ULONG MD5_DWORDS = MD5_BYTES / sizeof(DWORD);
 | |
|   UniquePtr<DWORD[]> hash;
 | |
| 
 | |
|   BCRYPT_ALG_HANDLE hAlg = nullptr;
 | |
|   if (NT_SUCCESS(::BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_MD5_ALGORITHM,
 | |
|                                                nullptr, 0))) {
 | |
|     BCRYPT_HASH_HANDLE hHash = nullptr;
 | |
|     // As of Windows 7 the hash handle will manage its own object buffer when
 | |
|     // pbHashObject is nullptr and cbHashObject is 0.
 | |
|     if (NT_SUCCESS(
 | |
|             ::BCryptCreateHash(hAlg, &hHash, nullptr, 0, nullptr, 0, 0))) {
 | |
|       // BCryptHashData promises not to modify pbInput.
 | |
|       if (NT_SUCCESS(::BCryptHashData(hHash, const_cast<unsigned char*>(bytes),
 | |
|                                       bytesLen, 0))) {
 | |
|         hash = MakeUnique<DWORD[]>(MD5_DWORDS);
 | |
|         if (!NT_SUCCESS(::BCryptFinishHash(
 | |
|                 hHash, reinterpret_cast<unsigned char*>(hash.get()),
 | |
|                 MD5_DWORDS * sizeof(DWORD), 0))) {
 | |
|           hash.reset();
 | |
|         }
 | |
|       }
 | |
|       ::BCryptDestroyHash(hHash);
 | |
|     }
 | |
|     ::BCryptCloseAlgorithmProvider(hAlg, 0);
 | |
|   }
 | |
| 
 | |
|   return hash;
 | |
| }
 | |
| 
 | |
| // @return The input bytes encoded as base64, nullptr on failure.
 | |
| static UniquePtr<wchar_t[]> CryptoAPI_Base64Encode(const unsigned char* bytes,
 | |
|                                                    DWORD bytesLen) {
 | |
|   DWORD base64Len = 0;
 | |
|   if (!::CryptBinaryToStringW(bytes, bytesLen,
 | |
|                               CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
 | |
|                               nullptr, &base64Len)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   auto base64 = MakeUnique<wchar_t[]>(base64Len);
 | |
|   if (!::CryptBinaryToStringW(bytes, bytesLen,
 | |
|                               CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
 | |
|                               base64.get(), &base64Len)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return base64;
 | |
| }
 | |
| 
 | |
| static inline DWORD WordSwap(DWORD v) { return (v >> 16) | (v << 16); }
 | |
| 
 | |
| /*
 | |
|  * Generate the UserChoice Hash.
 | |
|  *
 | |
|  * This implementation is based on the references listed above.
 | |
|  * It is organized to show the logic as clearly as possible, but at some
 | |
|  * point the reasoning is just "this is how it works".
 | |
|  *
 | |
|  * @param inputString   A null-terminated string to hash.
 | |
|  *
 | |
|  * @return The base64-encoded hash, or nullptr on failure.
 | |
|  */
 | |
| static UniquePtr<wchar_t[]> HashString(const wchar_t* inputString) {
 | |
|   auto inputBytes = reinterpret_cast<const unsigned char*>(inputString);
 | |
|   int inputByteCount = (::lstrlenW(inputString) + 1) * sizeof(wchar_t);
 | |
| 
 | |
|   constexpr size_t DWORDS_PER_BLOCK = 2;
 | |
|   constexpr size_t BLOCK_SIZE = sizeof(DWORD) * DWORDS_PER_BLOCK;
 | |
|   // Incomplete blocks are ignored.
 | |
|   int blockCount = inputByteCount / BLOCK_SIZE;
 | |
| 
 | |
|   if (blockCount == 0) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Compute an MD5 hash. md5[0] and md5[1] will be used as constant multipliers
 | |
|   // in the scramble below.
 | |
|   auto md5 = CNG_MD5(inputBytes, inputByteCount);
 | |
|   if (!md5) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // The following loop effectively computes two checksums, scrambled like a
 | |
|   // hash after every DWORD is added.
 | |
| 
 | |
|   // Constant multipliers for the scramble, one set for each DWORD in a block.
 | |
|   const DWORD C0s[DWORDS_PER_BLOCK][5] = {
 | |
|       {md5[0] | 1, 0xCF98B111uL, 0x87085B9FuL, 0x12CEB96DuL, 0x257E1D83uL},
 | |
|       {md5[1] | 1, 0xA27416F5uL, 0xD38396FFuL, 0x7C932B89uL, 0xBFA49F69uL}};
 | |
|   const DWORD C1s[DWORDS_PER_BLOCK][5] = {
 | |
|       {md5[0] | 1, 0xEF0569FBuL, 0x689B6B9FuL, 0x79F8A395uL, 0xC3EFEA97uL},
 | |
|       {md5[1] | 1, 0xC31713DBuL, 0xDDCD1F0FuL, 0x59C3AF2DuL, 0x35BD1EC9uL}};
 | |
| 
 | |
|   // The checksums.
 | |
|   DWORD h0 = 0;
 | |
|   DWORD h1 = 0;
 | |
|   // Accumulated total of the checksum after each DWORD.
 | |
|   DWORD h0Acc = 0;
 | |
|   DWORD h1Acc = 0;
 | |
| 
 | |
|   for (int i = 0; i < blockCount; ++i) {
 | |
|     for (size_t j = 0; j < DWORDS_PER_BLOCK; ++j) {
 | |
|       const DWORD* C0 = C0s[j];
 | |
|       const DWORD* C1 = C1s[j];
 | |
| 
 | |
|       DWORD input;
 | |
|       memcpy(&input, &inputBytes[(i * DWORDS_PER_BLOCK + j) * sizeof(DWORD)],
 | |
|              sizeof(DWORD));
 | |
| 
 | |
|       h0 += input;
 | |
|       // Scramble 0
 | |
|       h0 *= C0[0];
 | |
|       h0 = WordSwap(h0) * C0[1];
 | |
|       h0 = WordSwap(h0) * C0[2];
 | |
|       h0 = WordSwap(h0) * C0[3];
 | |
|       h0 = WordSwap(h0) * C0[4];
 | |
|       h0Acc += h0;
 | |
| 
 | |
|       h1 += input;
 | |
|       // Scramble 1
 | |
|       h1 = WordSwap(h1) * C1[1] + h1 * C1[0];
 | |
|       h1 = (h1 >> 16) * C1[2] + h1 * C1[3];
 | |
|       h1 = WordSwap(h1) * C1[4] + h1;
 | |
|       h1Acc += h1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DWORD hash[2] = {h0 ^ h1, h0Acc ^ h1Acc};
 | |
| 
 | |
|   return CryptoAPI_Base64Encode(reinterpret_cast<const unsigned char*>(hash),
 | |
|                                 sizeof(hash));
 | |
| }
 | |
| 
 | |
| UniquePtr<wchar_t[]> GetAssociationKeyPath(const wchar_t* aExt) {
 | |
|   const wchar_t* keyPathFmt;
 | |
|   if (aExt[0] == L'.') {
 | |
|     keyPathFmt =
 | |
|         L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s";
 | |
|   } else {
 | |
|     keyPathFmt =
 | |
|         L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\"
 | |
|         L"UrlAssociations\\%s";
 | |
|   }
 | |
| 
 | |
|   int keyPathLen = _scwprintf(keyPathFmt, aExt);
 | |
|   keyPathLen += 1;  // _scwprintf does not include the terminator
 | |
| 
 | |
|   auto keyPath = MakeUnique<wchar_t[]>(keyPathLen);
 | |
|   _snwprintf_s(keyPath.get(), keyPathLen, _TRUNCATE, keyPathFmt, aExt);
 | |
| 
 | |
|   return keyPath;
 | |
| }
 | |
| 
 | |
| UniquePtr<wchar_t[]> GenerateUserChoiceHash(const wchar_t* aExt,
 | |
|                                             const wchar_t* aUserSid,
 | |
|                                             const wchar_t* aProgId,
 | |
|                                             SYSTEMTIME aTimestamp) {
 | |
|   auto userChoice = FormatUserChoiceString(aExt, aUserSid, aProgId, aTimestamp);
 | |
|   if (!userChoice) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return HashString(userChoice.get());
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NOTE: The passed-in current user SID is used here, instead of getting the SID
 | |
|  * for the owner of the key. We are assuming that this key in HKCU is owned by
 | |
|  * the current user, since we want to replace that key ourselves. If the key is
 | |
|  * owned by someone else, then this check will fail; this is ok because we would
 | |
|  * likely not want to replace that other user's key anyway.
 | |
|  */
 | |
| CheckUserChoiceHashResult CheckUserChoiceHash(const wchar_t* aExt,
 | |
|                                               const wchar_t* aUserSid) {
 | |
|   auto keyPath = GetAssociationKeyPath(aExt);
 | |
|   if (!keyPath) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
| 
 | |
|   HKEY rawAssocKey;
 | |
|   if (::RegOpenKeyExW(HKEY_CURRENT_USER, keyPath.get(), 0, KEY_READ,
 | |
|                       &rawAssocKey) != ERROR_SUCCESS) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
|   nsAutoRegKey assocKey(rawAssocKey);
 | |
| 
 | |
|   FILETIME lastWriteFileTime;
 | |
|   {
 | |
|     HKEY rawUserChoiceKey;
 | |
|     if (::RegOpenKeyExW(assocKey.get(), L"UserChoice", 0, KEY_READ,
 | |
|                         &rawUserChoiceKey) != ERROR_SUCCESS) {
 | |
|       return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|     }
 | |
|     nsAutoRegKey userChoiceKey(rawUserChoiceKey);
 | |
| 
 | |
|     if (::RegQueryInfoKeyW(userChoiceKey.get(), nullptr, nullptr, nullptr,
 | |
|                            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
 | |
|                            nullptr, &lastWriteFileTime) != ERROR_SUCCESS) {
 | |
|       return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   SYSTEMTIME lastWriteSystemTime;
 | |
|   if (!::FileTimeToSystemTime(&lastWriteFileTime, &lastWriteSystemTime)) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
| 
 | |
|   // Read ProgId
 | |
|   DWORD dataSizeBytes = 0;
 | |
|   if (::RegGetValueW(assocKey.get(), L"UserChoice", L"ProgId", RRF_RT_REG_SZ,
 | |
|                      nullptr, nullptr, &dataSizeBytes) != ERROR_SUCCESS) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
|   // +1 in case dataSizeBytes was odd, +1 to ensure termination
 | |
|   DWORD dataSizeChars = (dataSizeBytes / sizeof(wchar_t)) + 2;
 | |
|   UniquePtr<wchar_t[]> progId(new wchar_t[dataSizeChars]());
 | |
|   if (::RegGetValueW(assocKey.get(), L"UserChoice", L"ProgId", RRF_RT_REG_SZ,
 | |
|                      nullptr, progId.get(), &dataSizeBytes) != ERROR_SUCCESS) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
| 
 | |
|   // Read Hash
 | |
|   dataSizeBytes = 0;
 | |
|   if (::RegGetValueW(assocKey.get(), L"UserChoice", L"Hash", RRF_RT_REG_SZ,
 | |
|                      nullptr, nullptr, &dataSizeBytes) != ERROR_SUCCESS) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
|   dataSizeChars = (dataSizeBytes / sizeof(wchar_t)) + 2;
 | |
|   UniquePtr<wchar_t[]> storedHash(new wchar_t[dataSizeChars]());
 | |
|   if (::RegGetValueW(assocKey.get(), L"UserChoice", L"Hash", RRF_RT_REG_SZ,
 | |
|                      nullptr, storedHash.get(),
 | |
|                      &dataSizeBytes) != ERROR_SUCCESS) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
| 
 | |
|   auto computedHash =
 | |
|       GenerateUserChoiceHash(aExt, aUserSid, progId.get(), lastWriteSystemTime);
 | |
|   if (!computedHash) {
 | |
|     return CheckUserChoiceHashResult::ERR_OTHER;
 | |
|   }
 | |
| 
 | |
|   if (::CompareStringOrdinal(computedHash.get(), -1, storedHash.get(), -1,
 | |
|                              FALSE) != CSTR_EQUAL) {
 | |
|     return CheckUserChoiceHashResult::ERR_MISMATCH;
 | |
|   }
 | |
| 
 | |
|   return CheckUserChoiceHashResult::OK_V1;
 | |
| }
 | |
| 
 | |
| bool CheckBrowserUserChoiceHashes() {
 | |
|   auto userSid = GetCurrentUserStringSid();
 | |
|   if (!userSid) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   const wchar_t* exts[] = {L"https", L"http", L".html", L".htm"};
 | |
| 
 | |
|   for (size_t i = 0; i < ArrayLength(exts); ++i) {
 | |
|     switch (CheckUserChoiceHash(exts[i], userSid.get())) {
 | |
|       case CheckUserChoiceHashResult::OK_V1:
 | |
|         break;
 | |
|       case CheckUserChoiceHashResult::ERR_MISMATCH:
 | |
|       case CheckUserChoiceHashResult::ERR_OTHER:
 | |
|         return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| UniquePtr<wchar_t[]> FormatProgID(const wchar_t* aProgIDBase,
 | |
|                                   const wchar_t* aAumi) {
 | |
|   const wchar_t* progIDFmt = L"%s-%s";
 | |
|   int progIDLen = _scwprintf(progIDFmt, aProgIDBase, aAumi);
 | |
|   progIDLen += 1;  // _scwprintf does not include the terminator
 | |
| 
 | |
|   auto progID = MakeUnique<wchar_t[]>(progIDLen);
 | |
|   _snwprintf_s(progID.get(), progIDLen, _TRUNCATE, progIDFmt, aProgIDBase,
 | |
|                aAumi);
 | |
| 
 | |
|   return progID;
 | |
| }
 | |
| 
 | |
| bool CheckProgIDExists(const wchar_t* aProgID) {
 | |
|   HKEY key;
 | |
|   if (::RegOpenKeyExW(HKEY_CLASSES_ROOT, aProgID, 0, KEY_READ, &key) !=
 | |
|       ERROR_SUCCESS) {
 | |
|     return false;
 | |
|   }
 | |
|   ::RegCloseKey(key);
 | |
|   return true;
 | |
| }
 |