gecko-dev/toolkit/xre/ModuleEvaluator.cpp
Aaron Klotz a66f824994 Bug 1542830: Part 6 - Rewrite the untrusted modules processor in toolkit/xre; r=mhowell
* 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
2019-09-23 20:19:17 +00:00

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