michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "registrycertificates.h" michael@0: #include "pathhash.h" michael@0: #include "nsWindowsHelpers.h" michael@0: #include "servicebase.h" michael@0: #include "updatehelper.h" michael@0: #define MAX_KEY_LENGTH 255 michael@0: michael@0: /** michael@0: * Verifies if the file path matches any certificate stored in the registry. michael@0: * michael@0: * @param filePath The file path of the application to check if allowed. michael@0: * @return TRUE if the binary matches any of the allowed certificates. michael@0: */ michael@0: BOOL michael@0: DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath) michael@0: { michael@0: WCHAR maintenanceServiceKey[MAX_PATH + 1]; michael@0: if (!CalculateRegistryPathFromFilePath(basePathForUpdate, michael@0: maintenanceServiceKey)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: // We use KEY_WOW64_64KEY to always force 64-bit view. michael@0: // The user may have both x86 and x64 applications installed michael@0: // which each register information. We need a consistent place michael@0: // to put those certificate attributes in and hence why we always michael@0: // force the non redirected registry under Wow6432Node. michael@0: // This flag is ignored on 32bit systems. michael@0: HKEY baseKeyRaw; michael@0: LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, michael@0: maintenanceServiceKey, 0, michael@0: KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Could not open key. (%d)", retCode)); michael@0: // Our tests run with a different apply directory for each test. michael@0: // We use this registry key on our test slaves to store the michael@0: // allowed name/issuers. michael@0: retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, michael@0: TEST_ONLY_FALLBACK_KEY_PATH, 0, michael@0: KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Could not open fallback key. (%d)", retCode)); michael@0: return FALSE; michael@0: } michael@0: } michael@0: nsAutoRegKey baseKey(baseKeyRaw); michael@0: michael@0: // Get the number of subkeys. michael@0: DWORD subkeyCount = 0; michael@0: retCode = RegQueryInfoKeyW(baseKey, nullptr, nullptr, nullptr, &subkeyCount, michael@0: nullptr, nullptr, nullptr, nullptr, nullptr, michael@0: nullptr, nullptr); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Could not query info key. (%d)", retCode)); michael@0: return FALSE; michael@0: } michael@0: michael@0: // Enumerate the subkeys, each subkey represents an allowed certificate. michael@0: for (DWORD i = 0; i < subkeyCount; i++) { michael@0: WCHAR subkeyBuffer[MAX_KEY_LENGTH]; michael@0: DWORD subkeyBufferCount = MAX_KEY_LENGTH; michael@0: retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer, michael@0: &subkeyBufferCount, nullptr, michael@0: nullptr, nullptr, nullptr); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Could not enum certs. (%d)", retCode)); michael@0: return FALSE; michael@0: } michael@0: michael@0: // Open the subkey for the current certificate michael@0: HKEY subKeyRaw; michael@0: retCode = RegOpenKeyExW(baseKey, michael@0: subkeyBuffer, michael@0: 0, michael@0: KEY_READ | KEY_WOW64_64KEY, michael@0: &subKeyRaw); michael@0: nsAutoRegKey subKey(subKeyRaw); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Could not open subkey. (%d)", retCode)); michael@0: continue; // Try the next subkey michael@0: } michael@0: michael@0: const int MAX_CHAR_COUNT = 256; michael@0: DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); michael@0: WCHAR name[MAX_CHAR_COUNT] = { L'\0' }; michael@0: WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' }; michael@0: michael@0: // Get the name from the registry michael@0: retCode = RegQueryValueExW(subKey, L"name", 0, nullptr, michael@0: (LPBYTE)name, &valueBufSize); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Could not obtain name from registry. (%d)", retCode)); michael@0: continue; // Try the next subkey michael@0: } michael@0: michael@0: // Get the issuer from the registry michael@0: valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); michael@0: retCode = RegQueryValueExW(subKey, L"issuer", 0, nullptr, michael@0: (LPBYTE)issuer, &valueBufSize); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Could not obtain issuer from registry. (%d)", retCode)); michael@0: continue; // Try the next subkey michael@0: } michael@0: michael@0: CertificateCheckInfo allowedCertificate = { michael@0: name, michael@0: issuer, michael@0: }; michael@0: michael@0: retCode = CheckCertificateForPEFile(filePath, allowedCertificate); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Error on certificate check. (%d)", retCode)); michael@0: continue; // Try the next subkey michael@0: } michael@0: michael@0: retCode = VerifyCertificateTrustForFile(filePath); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: LOG_WARN(("Error on certificate trust check. (%d)", retCode)); michael@0: continue; // Try the next subkey michael@0: } michael@0: michael@0: // Raise the roof, we found a match! michael@0: return TRUE; michael@0: } michael@0: michael@0: // No certificates match, :'( michael@0: return FALSE; michael@0: }