mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 18:26:15 +00:00
a66f824994
* Significant cleanup to `ModuleEvaluator` * `UntrustedModuleData` holds all of the accumulated untrusted module info for a single process. * `ProcessedModuleLoadEvent` holds information about an individual untrusted module load in a Gecko-friendly, sanitized, format. * Since multiple `ProcessModuleLoadEvent` objects may reference the same module, we store module metadata in a shared `ModuleInfo` structure. * The `UntrustedModulesProcessor` receives the events from `mozglue` and processes them on a background thread: ** It does not start background processing until the main thread has gone idle. The idea here is that we do not want to add any more background work until we are reasonably confident that Gecko is no longer starting up or doing other intense activity. ** Background processing runs at a background priority level, *except* when results are requested by telemetry itself. ** Telemetry requests the data via `UntrustedModulesProcessor::GetProcessedData` which runs at normal priority and returns a promise to the caller. Depends on D43159 Differential Revision: https://phabricator.services.mozilla.com/D43160 --HG-- rename : toolkit/xre/ModuleEvaluator_windows.cpp => toolkit/xre/ModuleEvaluator.cpp rename : toolkit/xre/ModuleEvaluator_windows.cpp => toolkit/xre/ModuleEvaluator.h rename : toolkit/xre/ModuleVersionInfo_windows.cpp => toolkit/xre/ModuleVersionInfo.cpp rename : toolkit/xre/ModuleVersionInfo_windows.h => toolkit/xre/ModuleVersionInfo.h rename : toolkit/xre/ModuleEvaluator_windows.cpp => toolkit/xre/UntrustedModulesData.cpp rename : toolkit/xre/ModuleEvaluator_windows.h => toolkit/xre/UntrustedModulesData.h rename : toolkit/xre/ModuleEvaluator_windows.cpp => toolkit/xre/UntrustedModulesProcessor.cpp rename : toolkit/xre/ModuleEvaluator_windows.h => toolkit/xre/UntrustedModulesProcessor.h extra : moz-landing-system : lando
246 lines
7.5 KiB
C++
246 lines
7.5 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ModuleEvaluator.h"
|
|
|
|
#include <algorithm> // For std::find()
|
|
|
|
#include <windows.h>
|
|
#include <shlobj.h>
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/ModuleVersionInfo.h"
|
|
#include "mozilla/TypeTraits.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "mozilla/WinDllServices.h"
|
|
#include "mozilla/WinHeaderOnlyUtils.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsWindowsHelpers.h"
|
|
#include "nsXULAppAPI.h"
|
|
|
|
// Fills a Vector with keyboard layout DLLs found in the registry.
|
|
// These are leaf names only, not full paths. Here we will convert them to
|
|
// lowercase before returning, to facilitate case-insensitive searches.
|
|
// On error, this may return partial results.
|
|
static Vector<nsString> GetKeyboardLayoutDlls() {
|
|
Vector<nsString> result;
|
|
|
|
HKEY rawKey;
|
|
if (::RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts",
|
|
0, KEY_ENUMERATE_SUB_KEYS, &rawKey) != ERROR_SUCCESS) {
|
|
return result;
|
|
}
|
|
nsAutoRegKey key(rawKey);
|
|
|
|
DWORD iKey = 0;
|
|
wchar_t strTemp[MAX_PATH] = {};
|
|
while (true) {
|
|
DWORD strTempSize = ArrayLength(strTemp);
|
|
if (RegEnumKeyExW(rawKey, iKey, strTemp, &strTempSize, nullptr, nullptr,
|
|
nullptr, nullptr) != ERROR_SUCCESS) {
|
|
// ERROR_NO_MORE_ITEMS or a real error: bail with what we have.
|
|
return result;
|
|
}
|
|
iKey++;
|
|
|
|
strTempSize = sizeof(strTemp);
|
|
if (::RegGetValueW(rawKey, strTemp, L"Layout File", RRF_RT_REG_SZ, nullptr,
|
|
strTemp, &strTempSize) == ERROR_SUCCESS &&
|
|
strTempSize) {
|
|
nsString ws(strTemp, ((strTempSize + 1) / sizeof(wchar_t)) - 1);
|
|
ToLowerCase(ws); // To facilitate case-insensitive searches
|
|
Unused << result.emplaceBack(std::move(ws));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
namespace mozilla {
|
|
|
|
/* static */
|
|
bool ModuleEvaluator::ResolveKnownFolder(REFKNOWNFOLDERID aFolderId,
|
|
nsIFile** aOutFile) {
|
|
if (!aOutFile) {
|
|
return false;
|
|
}
|
|
|
|
*aOutFile = nullptr;
|
|
|
|
// Since we're running off main thread, we can't use NS_GetSpecialDirectory
|
|
PWSTR rawPath = nullptr;
|
|
HRESULT hr =
|
|
::SHGetKnownFolderPath(aFolderId, KF_FLAG_DEFAULT, nullptr, &rawPath);
|
|
if (FAILED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
using ShellStringUniquePtr =
|
|
UniquePtr<RemovePointer<PWSTR>::Type, CoTaskMemFreeDeleter>;
|
|
|
|
ShellStringUniquePtr path(rawPath);
|
|
|
|
nsresult rv = NS_NewLocalFile(nsDependentString(path.get()), false, aOutFile);
|
|
return NS_SUCCEEDED(rv);
|
|
}
|
|
|
|
ModuleEvaluator::ModuleEvaluator()
|
|
: mKeyboardLayoutDlls(GetKeyboardLayoutDlls()) {
|
|
#if defined(_M_IX86)
|
|
// We want to resolve to SYSWOW64 when applicable
|
|
REFKNOWNFOLDERID systemFolderId = FOLDERID_SystemX86;
|
|
#else
|
|
REFKNOWNFOLDERID systemFolderId = FOLDERID_System;
|
|
#endif // defined(_M_IX86)
|
|
|
|
bool resolveOk =
|
|
ResolveKnownFolder(systemFolderId, getter_AddRefs(mSysDirectory));
|
|
MOZ_ASSERT(resolveOk);
|
|
if (!resolveOk) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIFile> winSxSDir;
|
|
resolveOk = ResolveKnownFolder(FOLDERID_Windows, getter_AddRefs(winSxSDir));
|
|
MOZ_ASSERT(resolveOk);
|
|
if (!resolveOk) {
|
|
return;
|
|
}
|
|
|
|
nsresult rv = winSxSDir->Append(NS_LITERAL_STRING("WinSxS"));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
mWinSxSDirectory = std::move(winSxSDir);
|
|
|
|
nsCOMPtr<nsIFile> exeFile;
|
|
rv = XRE_GetBinaryPath(getter_AddRefs(exeFile));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
rv = exeFile->GetParent(getter_AddRefs(mExeDirectory));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
nsAutoString exePath;
|
|
rv = exeFile->GetPath(exePath);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
ModuleVersionInfo exeVi;
|
|
if (!exeVi.GetFromImage(exePath)) {
|
|
return;
|
|
}
|
|
|
|
mExeVersion = Some(ModuleVersion(exeVi.mFileVersion.Version64()));
|
|
}
|
|
|
|
ModuleEvaluator::operator bool() const {
|
|
return mExeVersion.isSome() && mExeDirectory && mSysDirectory &&
|
|
mWinSxSDirectory;
|
|
}
|
|
|
|
Maybe<ModuleTrustFlags> ModuleEvaluator::GetTrust(
|
|
const ModuleRecord& aModuleRecord) const {
|
|
// We start by checking authenticode signatures, as the presence of any
|
|
// signature will produce an immediate pass/fail.
|
|
if (aModuleRecord.mVendorInfo.isSome() &&
|
|
aModuleRecord.mVendorInfo.ref().mSource ==
|
|
VendorInfo::Source::Signature) {
|
|
const nsString& signedBy = aModuleRecord.mVendorInfo.ref().mVendor;
|
|
|
|
if (signedBy.EqualsLiteral("Microsoft Windows")) {
|
|
return Some(ModuleTrustFlags::MicrosoftWindowsSignature);
|
|
} else if (signedBy.EqualsLiteral("Microsoft Corporation")) {
|
|
return Some(ModuleTrustFlags::MicrosoftWindowsSignature);
|
|
} else if (signedBy.EqualsLiteral("Mozilla Corporation")) {
|
|
return Some(ModuleTrustFlags::MozillaSignature);
|
|
} else {
|
|
// Being signed by somebody who is neither Microsoft nor us is an
|
|
// automatic and immediate disqualification.
|
|
return Some(ModuleTrustFlags::None);
|
|
}
|
|
}
|
|
|
|
const nsCOMPtr<nsIFile>& dllFile = aModuleRecord.mResolvedDllName;
|
|
|
|
nsAutoString dllLeafLower;
|
|
if (NS_FAILED(dllFile->GetLeafName(dllLeafLower))) {
|
|
return Nothing();
|
|
}
|
|
|
|
ToLowerCase(dllLeafLower); // To facilitate case-insensitive searching
|
|
|
|
// The JIT profiling module doesn't really have any other practical way to
|
|
// match; hard-code it as being trusted.
|
|
if (dllLeafLower.EqualsLiteral("jitpi.dll")) {
|
|
return Some(ModuleTrustFlags::JitPI);
|
|
}
|
|
|
|
ModuleTrustFlags result = ModuleTrustFlags::None;
|
|
|
|
nsresult rv;
|
|
bool contained;
|
|
|
|
// Is the DLL in the system directory?
|
|
rv = mSysDirectory->Contains(dllFile, &contained);
|
|
if (NS_SUCCEEDED(rv) && contained) {
|
|
result |= ModuleTrustFlags::SystemDirectory;
|
|
}
|
|
|
|
// Is the DLL in the WinSxS directory? Some Microsoft DLLs (e.g. comctl32) are
|
|
// loaded from here and don't have digital signatures. So while this is not a
|
|
// guarantee of trustworthiness, but is at least as valid as system32.
|
|
rv = mWinSxSDirectory->Contains(dllFile, &contained);
|
|
if (NS_SUCCEEDED(rv) && contained) {
|
|
result |= ModuleTrustFlags::WinSxSDirectory;
|
|
}
|
|
|
|
// Is it a keyboard layout DLL?
|
|
if (std::find(mKeyboardLayoutDlls.begin(), mKeyboardLayoutDlls.end(),
|
|
dllLeafLower) != mKeyboardLayoutDlls.end()) {
|
|
result |= ModuleTrustFlags::KeyboardLayout;
|
|
// This doesn't guarantee trustworthiness by itself. Keyboard layouts also
|
|
// must be in the system directory.
|
|
}
|
|
|
|
if (aModuleRecord.mVendorInfo.isSome() &&
|
|
aModuleRecord.mVendorInfo.ref().mSource ==
|
|
VendorInfo::Source::VersionInfo) {
|
|
const nsString& companyName = aModuleRecord.mVendorInfo.ref().mVendor;
|
|
|
|
if (companyName.EqualsLiteral("Microsoft Corporation")) {
|
|
result |= ModuleTrustFlags::MicrosoftVersion;
|
|
}
|
|
}
|
|
|
|
rv = mExeDirectory->Contains(dllFile, &contained);
|
|
if (NS_SUCCEEDED(rv) && contained) {
|
|
result |= ModuleTrustFlags::FirefoxDirectory;
|
|
|
|
// If the DLL is in the Firefox directory, does it also share the Firefox
|
|
// version info?
|
|
if (mExeVersion.isSome() && aModuleRecord.mVersion.isSome() &&
|
|
mExeVersion.value() == aModuleRecord.mVersion.value()) {
|
|
result |= ModuleTrustFlags::FirefoxDirectoryAndVersion;
|
|
}
|
|
}
|
|
|
|
return Some(result);
|
|
}
|
|
|
|
} // namespace mozilla
|