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_windows.h"
10 #include <algorithm> // For std::find()
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/UniquePtr.h"
14 #include "mozilla/Unused.h"
16 #include "nsDirectoryServiceDefs.h"
18 #include "nsReadableUtils.h"
19 #include "nsWindowsHelpers.h"
24 // Utility function to get the parent directory of aFile.
25 // Returns true upon success.
27 GetDirectoryName(const nsCOMPtr
<nsIFile
> aFile
, nsAString
& aParent
)
29 nsCOMPtr
<nsIFile
> parentDir
;
30 if (NS_FAILED(aFile
->GetParent(getter_AddRefs(parentDir
))) || !parentDir
) {
33 if (NS_FAILED(parentDir
->GetPath(aParent
))) {
39 ModuleLoadEvent::ModuleInfo::ModuleInfo(const glue::ModuleLoadEvent::ModuleInfo
& aOther
)
42 if (aOther
.mLdrName
) {
43 mLdrName
.Assign(aOther
.mLdrName
.get());
45 if (aOther
.mFullPath
) {
46 nsDependentString
tempPath(aOther
.mFullPath
.get());
47 Unused
<< NS_NewLocalFile(tempPath
, false, getter_AddRefs(mFile
));
51 ModuleLoadEvent::ModuleLoadEvent(const ModuleLoadEvent
& aOther
, CopyOption aOption
)
52 : mIsStartup(aOther
.mIsStartup
)
53 , mThreadID(aOther
.mThreadID
)
54 , mThreadName(aOther
.mThreadName
)
55 , mProcessUptimeMS(aOther
.mProcessUptimeMS
)
57 Unused
<< mStack
.reserve(aOther
.mStack
.length());
58 for (auto& x
: aOther
.mStack
) {
59 Unused
<< mStack
.append(x
);
61 if (aOption
!= CopyOption::CopyWithoutModules
) {
62 Unused
<< mModules
.reserve(aOther
.mModules
.length());
63 for (auto& x
: aOther
.mModules
) {
64 Unused
<< mModules
.append(x
);
69 ModuleLoadEvent::ModuleLoadEvent(const glue::ModuleLoadEvent
& aOther
)
70 : mIsStartup(false) // Events originating in glue:: cannot be a startup event.
71 , mThreadID(aOther
.mThreadID
)
72 , mProcessUptimeMS(aOther
.mProcessUptimeMS
)
74 for (auto& frame
: aOther
.mStack
) {
75 Unused
<< mStack
.append(frame
);
77 for (auto& module
: aOther
.mModules
) {
78 Unused
<< mModules
.append(ModuleInfo(module
));
82 // Fills a Vector with keyboard layout DLLs found in the registry.
83 // These are leaf names only, not full paths. Here we will convert them to
84 // lowercase before returning, to facilitate case-insensitive searches.
85 // On error, this may return partial results.
87 GetKeyboardLayoutDlls(Vector
<nsString
, 0, InfallibleAllocPolicy
>& aOut
)
90 if (::RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
91 L
"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts",
92 0, KEY_ENUMERATE_SUB_KEYS
, &rawKey
) != ERROR_SUCCESS
) {
95 nsAutoRegKey
key(rawKey
);
98 wchar_t strTemp
[MAX_PATH
] = {0};
100 DWORD strTempSize
= ArrayLength(strTemp
);
101 if (RegEnumKeyExW(rawKey
, iKey
, strTemp
, &strTempSize
,
102 nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS
) {
103 return; // ERROR_NO_MORE_ITEMS or a real error: bail with what we have.
107 strTempSize
= sizeof(strTemp
);
108 if (::RegGetValueW(rawKey
, strTemp
, L
"Layout File",
109 RRF_RT_REG_SZ
, nullptr, strTemp
,
110 &strTempSize
) == ERROR_SUCCESS
) {
111 nsString
ws(strTemp
, (strTempSize
/ sizeof(wchar_t)) - 1);
112 ToLowerCase(ws
); // To facilitate searches
113 Unused
<< aOut
.emplaceBack(ws
);
118 ModuleEvaluator::ModuleEvaluator()
120 GetKeyboardLayoutDlls(mKeyboardLayoutDlls
);
122 nsCOMPtr
<nsIFile
> sysDir
;
123 if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_OS_SYSTEM_DIR
,
124 getter_AddRefs(sysDir
)))) {
125 sysDir
->GetPath(mSysDirectory
);
128 nsCOMPtr
<nsIFile
> exeDir
;
129 if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_GRE_DIR
,
130 getter_AddRefs(exeDir
)))) {
131 exeDir
->GetPath(mExeDirectory
);
134 nsCOMPtr
<nsIFile
> exeFile
;
135 if (NS_SUCCEEDED(XRE_GetBinaryPath(getter_AddRefs(exeFile
)))) {
136 nsAutoString exePath
;
137 if (NS_SUCCEEDED(exeFile
->GetPath(exePath
))) {
138 ModuleVersionInfo exeVi
;
139 if (exeVi
.GetFromImage(exePath
)) {
140 mExeVersion
= Some(exeVi
.mFileVersion
.Version64());
147 ModuleEvaluator::IsModuleTrusted(ModuleLoadEvent::ModuleInfo
& aDllInfo
,
148 const ModuleLoadEvent
& aEvent
,
149 Authenticode
* aSvc
) const
151 // The JIT profiling module doesn't really have any other practical way to
152 // match; hard-code it as being trusted.
153 if (aDllInfo
.mLdrName
.EqualsLiteral("JitPI.dll")) {
154 aDllInfo
.mTrustFlags
= ModuleTrustFlags::JitPI
;
158 aDllInfo
.mTrustFlags
= ModuleTrustFlags::None
;
160 if (!aDllInfo
.mFile
) {
161 return Nothing(); // Every check here depends on having a valid image file.
164 using PathTransformFlags
= widget::WinUtils::PathTransformFlags
;
166 Unused
<< widget::WinUtils::PreparePathForTelemetry(aDllInfo
.mLdrName
,
167 PathTransformFlags::Default
& ~PathTransformFlags::Canonicalize
);
169 nsAutoString dllFullPath
;
170 if (NS_FAILED(aDllInfo
.mFile
->GetPath(dllFullPath
))) {
173 widget::WinUtils::MakeLongPath(dllFullPath
);
175 aDllInfo
.mFilePathClean
= dllFullPath
;
176 if (!widget::WinUtils::PreparePathForTelemetry(aDllInfo
.mFilePathClean
,
177 PathTransformFlags::Default
&
178 ~(PathTransformFlags::Canonicalize
| PathTransformFlags::Lengthen
))) {
182 if (NS_FAILED(NS_NewLocalFile(dllFullPath
, false,
183 getter_AddRefs(aDllInfo
.mFile
)))) {
187 nsAutoString dllDirectory
;
188 if (!GetDirectoryName(aDllInfo
.mFile
, dllDirectory
)) {
192 nsAutoString dllLeafLower
;
193 if (NS_FAILED(aDllInfo
.mFile
->GetLeafName(dllLeafLower
))) {
196 ToLowerCase(dllLeafLower
); // To facilitate case-insensitive searching
199 int scoreThreshold
= 100;
200 // For testing, these DLLs are hardcoded to pass through all criteria checks
201 // and still result in "untrusted" status.
202 if (dllLeafLower
.EqualsLiteral("mozglue.dll") ||
203 dllLeafLower
.EqualsLiteral("modules-test.dll")) {
204 scoreThreshold
= 99999;
207 static const int scoreThreshold
= 100;
212 // Is the DLL in the system directory?
213 if (!mSysDirectory
.IsEmpty() && StringBeginsWith(dllFullPath
, mSysDirectory
,
214 nsCaseInsensitiveStringComparator())) {
215 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::SystemDirectory
;
219 // Is it a keyboard layout DLL?
220 if (std::find(mKeyboardLayoutDlls
.begin(),
221 mKeyboardLayoutDlls
.end(),
222 dllLeafLower
) != mKeyboardLayoutDlls
.end()) {
223 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::KeyboardLayout
;
224 // This doesn't guarantee trustworthiness by itself. Keyboard layouts also
225 // must be in the system directory, which will bump the score >= 100.
229 if (score
< scoreThreshold
) {
230 ModuleVersionInfo vi
;
231 if (vi
.GetFromImage(dllFullPath
)) {
232 aDllInfo
.mFileVersion
= vi
.mFileVersion
.ToString();
234 if (vi
.mCompanyName
.EqualsLiteral("Microsoft Corporation")) {
235 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::MicrosoftVersion
;
239 if (!mExeDirectory
.IsEmpty() && StringBeginsWith(dllFullPath
, mExeDirectory
,
240 nsCaseInsensitiveStringComparator())) {
242 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::FirefoxDirectory
;
244 // If it's in the Firefox directory, does it also share the Firefox
245 // version info? We only care about this inside the app directory.
246 if (mExeVersion
.isSome() &&
247 (vi
.mFileVersion
.Version64() == mExeVersion
.value())) {
248 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::FirefoxDirectoryAndVersion
;
255 if (score
< scoreThreshold
) {
257 UniquePtr
<wchar_t[]> szSignedBy
= aSvc
->GetBinaryOrgName(dllFullPath
.get());
259 nsAutoString
signedBy(szSignedBy
.get());
260 if (signedBy
.EqualsLiteral("Microsoft Windows")) {
261 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::MicrosoftWindowsSignature
;
263 } else if (signedBy
.EqualsLiteral("Microsoft Corporation")) {
264 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::MicrosoftWindowsSignature
;
266 } else if (signedBy
.EqualsLiteral("Mozilla Corporation")) {
267 aDllInfo
.mTrustFlags
|= ModuleTrustFlags::MozillaSignature
;
274 return Some(score
>= scoreThreshold
);
277 } // namespace mozilla