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
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #include "ModuleEvaluator.h"
9 #include <algorithm> // For std::find()
10 #include <type_traits>
15 #include "mozilla/ArrayUtils.h"
16 #include "mozilla/ModuleVersionInfo.h"
17 #include "mozilla/UniquePtr.h"
18 #include "mozilla/Unused.h"
19 #include "mozilla/WinDllServices.h"
20 #include "mozilla/WinHeaderOnlyUtils.h"
21 #include "nsReadableUtils.h"
22 #include "nsWindowsHelpers.h"
23 #include "nsXULAppAPI.h"
25 // Fills a Vector with keyboard layout DLLs found in the registry.
26 // These are leaf names only, not full paths. Here we will convert them to
27 // lowercase before returning, to facilitate case-insensitive searches.
28 // On error, this may return partial results.
29 static Vector
<nsString
> GetKeyboardLayoutDlls() {
30 Vector
<nsString
> result
;
33 if (::RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
34 L
"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts",
35 0, KEY_ENUMERATE_SUB_KEYS
, &rawKey
) != ERROR_SUCCESS
) {
38 nsAutoRegKey
key(rawKey
);
41 wchar_t strTemp
[MAX_PATH
] = {};
43 DWORD strTempSize
= ArrayLength(strTemp
);
44 if (RegEnumKeyExW(rawKey
, iKey
, strTemp
, &strTempSize
, nullptr, nullptr,
45 nullptr, nullptr) != ERROR_SUCCESS
) {
46 // ERROR_NO_MORE_ITEMS or a real error: bail with what we have.
51 strTempSize
= sizeof(strTemp
);
52 if (::RegGetValueW(rawKey
, strTemp
, L
"Layout File", RRF_RT_REG_SZ
, nullptr,
53 strTemp
, &strTempSize
) == ERROR_SUCCESS
&&
55 nsString
ws(strTemp
, ((strTempSize
+ 1) / sizeof(wchar_t)) - 1);
56 ToLowerCase(ws
); // To facilitate case-insensitive searches
57 Unused
<< result
.emplaceBack(std::move(ws
));
67 bool ModuleEvaluator::ResolveKnownFolder(REFKNOWNFOLDERID aFolderId
,
75 // Since we're running off main thread, we can't use NS_GetSpecialDirectory
76 PWSTR rawPath
= nullptr;
78 ::SHGetKnownFolderPath(aFolderId
, KF_FLAG_DEFAULT
, nullptr, &rawPath
);
83 using ShellStringUniquePtr
=
84 UniquePtr
<std::remove_pointer_t
<PWSTR
>, CoTaskMemFreeDeleter
>;
86 ShellStringUniquePtr
path(rawPath
);
88 nsresult rv
= NS_NewLocalFile(nsDependentString(path
.get()), false, aOutFile
);
89 return NS_SUCCEEDED(rv
);
92 ModuleEvaluator::ModuleEvaluator()
93 : mKeyboardLayoutDlls(GetKeyboardLayoutDlls()) {
94 MOZ_ASSERT(XRE_IsParentProcess());
97 // We want to resolve to SYSWOW64 when applicable
98 REFKNOWNFOLDERID systemFolderId
= FOLDERID_SystemX86
;
100 REFKNOWNFOLDERID systemFolderId
= FOLDERID_System
;
101 #endif // defined(_M_IX86)
104 ResolveKnownFolder(systemFolderId
, getter_AddRefs(mSysDirectory
));
105 MOZ_ASSERT(resolveOk
);
110 nsCOMPtr
<nsIFile
> winSxSDir
;
111 resolveOk
= ResolveKnownFolder(FOLDERID_Windows
, getter_AddRefs(winSxSDir
));
112 MOZ_ASSERT(resolveOk
);
117 nsresult rv
= winSxSDir
->Append(u
"WinSxS"_ns
);
118 MOZ_ASSERT(NS_SUCCEEDED(rv
));
123 mWinSxSDirectory
= std::move(winSxSDir
);
125 nsCOMPtr
<nsIFile
> exeFile
;
126 rv
= XRE_GetBinaryPath(getter_AddRefs(exeFile
));
127 MOZ_ASSERT(NS_SUCCEEDED(rv
));
132 rv
= exeFile
->GetParent(getter_AddRefs(mExeDirectory
));
133 MOZ_ASSERT(NS_SUCCEEDED(rv
));
138 nsAutoString exePath
;
139 rv
= exeFile
->GetPath(exePath
);
140 MOZ_ASSERT(NS_SUCCEEDED(rv
));
145 ModuleVersionInfo exeVi
;
146 if (!exeVi
.GetFromImage(exePath
)) {
150 mExeVersion
= Some(ModuleVersion(exeVi
.mFileVersion
.Version64()));
153 ModuleEvaluator::operator bool() const {
154 return mExeVersion
.isSome() && mExeDirectory
&& mSysDirectory
&&
158 Maybe
<ModuleTrustFlags
> ModuleEvaluator::GetTrust(
159 const ModuleRecord
& aModuleRecord
) const {
160 MOZ_ASSERT(XRE_IsParentProcess());
162 // We start by checking authenticode signatures, as the presence of any
163 // signature will produce an immediate pass/fail.
164 if (aModuleRecord
.mVendorInfo
.isSome() &&
165 aModuleRecord
.mVendorInfo
.ref().mSource
==
166 VendorInfo::Source::Signature
) {
167 const nsString
& signedBy
= aModuleRecord
.mVendorInfo
.ref().mVendor
;
169 if (signedBy
.EqualsLiteral("Microsoft Windows")) {
170 return Some(ModuleTrustFlags::MicrosoftWindowsSignature
);
171 } else if (signedBy
.EqualsLiteral("Microsoft Corporation")) {
172 return Some(ModuleTrustFlags::MicrosoftWindowsSignature
);
173 } else if (signedBy
.EqualsLiteral("Mozilla Corporation")) {
174 return Some(ModuleTrustFlags::MozillaSignature
);
176 // Being signed by somebody who is neither Microsoft nor us is an
177 // automatic and immediate disqualification.
178 return Some(ModuleTrustFlags::None
);
182 const nsCOMPtr
<nsIFile
>& dllFile
= aModuleRecord
.mResolvedDosName
;
183 MOZ_ASSERT(!!dllFile
);
188 nsAutoString dllLeafLower
;
189 if (NS_FAILED(dllFile
->GetLeafName(dllLeafLower
))) {
193 ToLowerCase(dllLeafLower
); // To facilitate case-insensitive searching
195 // The JIT profiling module doesn't really have any other practical way to
196 // match; hard-code it as being trusted.
197 if (dllLeafLower
.EqualsLiteral("jitpi.dll")) {
198 return Some(ModuleTrustFlags::JitPI
);
201 ModuleTrustFlags result
= ModuleTrustFlags::None
;
206 // Is the DLL in the system directory?
207 rv
= mSysDirectory
->Contains(dllFile
, &contained
);
208 if (NS_SUCCEEDED(rv
) && contained
) {
209 result
|= ModuleTrustFlags::SystemDirectory
;
212 // Is the DLL in the WinSxS directory? Some Microsoft DLLs (e.g. comctl32) are
213 // loaded from here and don't have digital signatures. So while this is not a
214 // guarantee of trustworthiness, but is at least as valid as system32.
215 rv
= mWinSxSDirectory
->Contains(dllFile
, &contained
);
216 if (NS_SUCCEEDED(rv
) && contained
) {
217 result
|= ModuleTrustFlags::WinSxSDirectory
;
220 // Is it a keyboard layout DLL?
221 if (std::find(mKeyboardLayoutDlls
.begin(), mKeyboardLayoutDlls
.end(),
222 dllLeafLower
) != mKeyboardLayoutDlls
.end()) {
223 result
|= ModuleTrustFlags::KeyboardLayout
;
224 // This doesn't guarantee trustworthiness by itself. Keyboard layouts also
225 // must be in the system directory.
228 if (aModuleRecord
.mVendorInfo
.isSome() &&
229 aModuleRecord
.mVendorInfo
.ref().mSource
==
230 VendorInfo::Source::VersionInfo
) {
231 const nsString
& companyName
= aModuleRecord
.mVendorInfo
.ref().mVendor
;
233 if (companyName
.EqualsLiteral("Microsoft Corporation")) {
234 result
|= ModuleTrustFlags::MicrosoftVersion
;
238 rv
= mExeDirectory
->Contains(dllFile
, &contained
);
239 if (NS_SUCCEEDED(rv
) && contained
) {
240 result
|= ModuleTrustFlags::FirefoxDirectory
;
242 // If the DLL is in the Firefox directory, does it also share the Firefox
244 if (mExeVersion
.isSome() && aModuleRecord
.mVersion
.isSome() &&
245 mExeVersion
.value() == aModuleRecord
.mVersion
.value()) {
246 result
|= ModuleTrustFlags::FirefoxDirectoryAndVersion
;
253 } // namespace mozilla