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 http://mozilla.org/MPL/2.0/. */
6 
7 #include "RegistrationAnnotator.h"
8 
9 #include "mozilla/JSONWriter.h"
10 #include "mozilla/mscom/Utils.h"
11 #include "mozilla/NotNull.h"
12 #include "nsExceptionHandler.h"
13 #include "nsPrintfCString.h"
14 #include "nsWindowsHelpers.h"
15 #include "nsXULAppAPI.h"
16 
17 #include <oleauto.h>
18 
19 namespace {
20 
21 class CStringWriter final : public mozilla::JSONWriteFunc {
22  public:
Write(const mozilla::Span<const char> & aStr)23   void Write(const mozilla::Span<const char>& aStr) override {
24     mBuf.Append(aStr);
25   }
26 
Get() const27   const nsCString& Get() const { return mBuf; }
28 
29  private:
30   nsCString mBuf;
31 };
32 
33 }  // anonymous namespace
34 
35 namespace mozilla {
36 namespace mscom {
37 
38 static const char16_t kSoftwareClasses[] = u"SOFTWARE\\Classes";
39 static const char16_t kInterface[] = u"\\Interface\\";
40 static const char16_t kDefaultValue[] = u"";
41 static const char16_t kThreadingModel[] = u"ThreadingModel";
42 static const char16_t kBackslash[] = u"\\";
43 static const char16_t kFlags[] = u"FLAGS";
44 static const char16_t kProxyStubClsid32[] = u"\\ProxyStubClsid32";
45 static const char16_t kClsid[] = u"\\CLSID\\";
46 static const char16_t kInprocServer32[] = u"\\InprocServer32";
47 static const char16_t kInprocHandler32[] = u"\\InprocHandler32";
48 static const char16_t kTypeLib[] = u"\\TypeLib";
49 static const char16_t kVersion[] = u"Version";
50 static const char16_t kWin32[] = u"Win32";
51 static const char16_t kWin64[] = u"Win64";
52 
GetStringValue(HKEY aBaseKey,const nsAString & aStrSubKey,const nsAString & aValueName,nsAString & aOutput)53 static bool GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
54                            const nsAString& aValueName, nsAString& aOutput) {
55   const nsString& flatSubKey = PromiseFlatString(aStrSubKey);
56   const nsString& flatValueName = PromiseFlatString(aValueName);
57   LPCWSTR valueName = aValueName.IsEmpty() ? nullptr : flatValueName.get();
58 
59   DWORD type = 0;
60   DWORD numBytes = 0;
61   LONG result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, RRF_RT_ANY,
62                             &type, nullptr, &numBytes);
63   if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
64     return false;
65   }
66 
67   int numChars = (numBytes + 1) / sizeof(wchar_t);
68   aOutput.SetLength(numChars);
69 
70   DWORD acceptFlag = type == REG_SZ ? RRF_RT_REG_SZ : RRF_RT_REG_EXPAND_SZ;
71 
72   result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, acceptFlag,
73                        nullptr, aOutput.BeginWriting(), &numBytes);
74   if (result == ERROR_SUCCESS) {
75     // Truncate null terminator
76     aOutput.SetLength(((numBytes + 1) / sizeof(wchar_t)) - 1);
77   }
78 
79   return result == ERROR_SUCCESS;
80 }
81 
82 template <size_t N>
GetStringValue(HKEY aBaseKey,const nsAString & aStrSubKey,const char16_t (& aValueName)[N],nsAString & aOutput)83 inline static bool GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
84                                   const char16_t (&aValueName)[N],
85                                   nsAString& aOutput) {
86   return GetStringValue(aBaseKey, aStrSubKey, nsLiteralString(aValueName),
87                         aOutput);
88 }
89 
90 /**
91  * This function fails unless the entire string has been converted.
92  * (eg, the string "FLAGS" will convert to 0xF but we will return false)
93  */
ConvertLCID(const wchar_t * aStr,NotNull<unsigned long * > aOutLcid)94 static bool ConvertLCID(const wchar_t* aStr, NotNull<unsigned long*> aOutLcid) {
95   wchar_t* endChar;
96   *aOutLcid = wcstoul(aStr, &endChar, 16);
97   return *endChar == 0;
98 }
99 
GetLoadedPath(nsAString & aPath)100 static bool GetLoadedPath(nsAString& aPath) {
101   // These paths may be REG_EXPAND_SZ, so we expand any environment strings
102   DWORD bufCharLen =
103       ExpandEnvironmentStrings(PromiseFlatString(aPath).get(), nullptr, 0);
104 
105   auto buf = MakeUnique<WCHAR[]>(bufCharLen);
106 
107   if (!ExpandEnvironmentStrings(PromiseFlatString(aPath).get(), buf.get(),
108                                 bufCharLen)) {
109     return false;
110   }
111 
112   // Use LoadLibrary so that the DLL is resolved using the loader's DLL search
113   // rules
114   nsModuleHandle mod(LoadLibrary(buf.get()));
115   if (!mod) {
116     return false;
117   }
118 
119   WCHAR finalPath[MAX_PATH + 1] = {};
120   DWORD result = GetModuleFileNameW(mod, finalPath, ArrayLength(finalPath));
121   if (!result || (result == ArrayLength(finalPath) &&
122                   GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
123     return false;
124   }
125 
126   aPath = nsDependentString(finalPath, result);
127   return true;
128 }
129 
AnnotateClsidRegistrationForHive(JSONWriter & aJson,HKEY aHive,const nsAString & aClsid,const JSONWriter::CollectionStyle aStyle)130 static void AnnotateClsidRegistrationForHive(
131     JSONWriter& aJson, HKEY aHive, const nsAString& aClsid,
132     const JSONWriter::CollectionStyle aStyle) {
133   nsAutoString clsidSubkey;
134   clsidSubkey.AppendLiteral(kSoftwareClasses);
135   clsidSubkey.AppendLiteral(kClsid);
136   clsidSubkey.Append(aClsid);
137 
138   nsAutoString className;
139   if (GetStringValue(aHive, clsidSubkey, kDefaultValue, className)) {
140     aJson.StringProperty("ClassName", NS_ConvertUTF16toUTF8(className));
141   }
142 
143   nsAutoString inprocServerSubkey(clsidSubkey);
144   inprocServerSubkey.AppendLiteral(kInprocServer32);
145 
146   nsAutoString pathToServerDll;
147   if (GetStringValue(aHive, inprocServerSubkey, kDefaultValue,
148                      pathToServerDll)) {
149     aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(pathToServerDll));
150     if (GetLoadedPath(pathToServerDll)) {
151       aJson.StringProperty("LoadedPath",
152                            NS_ConvertUTF16toUTF8(pathToServerDll));
153     }
154   }
155 
156   nsAutoString apartment;
157   if (GetStringValue(aHive, inprocServerSubkey, kThreadingModel, apartment)) {
158     aJson.StringProperty("ThreadingModel", NS_ConvertUTF16toUTF8(apartment));
159   }
160 
161   nsAutoString inprocHandlerSubkey(clsidSubkey);
162   inprocHandlerSubkey.AppendLiteral(kInprocHandler32);
163   nsAutoString pathToHandlerDll;
164   if (GetStringValue(aHive, inprocHandlerSubkey, kDefaultValue,
165                      pathToHandlerDll)) {
166     aJson.StringProperty("HandlerPath",
167                          NS_ConvertUTF16toUTF8(pathToHandlerDll));
168     if (GetLoadedPath(pathToHandlerDll)) {
169       aJson.StringProperty("LoadedHandlerPath",
170                            NS_ConvertUTF16toUTF8(pathToHandlerDll));
171     }
172   }
173 
174   nsAutoString handlerApartment;
175   if (GetStringValue(aHive, inprocHandlerSubkey, kThreadingModel,
176                      handlerApartment)) {
177     aJson.StringProperty("HandlerThreadingModel",
178                          NS_ConvertUTF16toUTF8(handlerApartment));
179   }
180 }
181 
CheckTlbPath(JSONWriter & aJson,const nsAString & aTypelibPath)182 static void CheckTlbPath(JSONWriter& aJson, const nsAString& aTypelibPath) {
183   const nsString& flatPath = PromiseFlatString(aTypelibPath);
184   DWORD bufCharLen = ExpandEnvironmentStrings(flatPath.get(), nullptr, 0);
185 
186   auto buf = MakeUnique<WCHAR[]>(bufCharLen);
187 
188   if (!ExpandEnvironmentStrings(flatPath.get(), buf.get(), bufCharLen)) {
189     return;
190   }
191 
192   // See whether this tlb can actually be loaded
193   RefPtr<ITypeLib> typeLib;
194   HRESULT hr = LoadTypeLibEx(buf.get(), REGKIND_NONE, getter_AddRefs(typeLib));
195 
196   nsPrintfCString loadResult("0x%08X", hr);
197   aJson.StringProperty("LoadResult", loadResult);
198 }
199 
200 template <size_t N>
AnnotateTypelibPlatform(JSONWriter & aJson,HKEY aBaseKey,const nsAString & aLcidSubkey,const char16_t (& aPlatform)[N],const JSONWriter::CollectionStyle aStyle)201 static void AnnotateTypelibPlatform(JSONWriter& aJson, HKEY aBaseKey,
202                                     const nsAString& aLcidSubkey,
203                                     const char16_t (&aPlatform)[N],
204                                     const JSONWriter::CollectionStyle aStyle) {
205   nsLiteralString platform(aPlatform);
206 
207   nsAutoString fullSubkey(aLcidSubkey);
208   fullSubkey.AppendLiteral(kBackslash);
209   fullSubkey.Append(platform);
210 
211   nsAutoString tlbPath;
212   if (GetStringValue(aBaseKey, fullSubkey, kDefaultValue, tlbPath)) {
213     aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(platform), aStyle);
214     aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(tlbPath));
215     CheckTlbPath(aJson, tlbPath);
216     aJson.EndObject();
217   }
218 }
219 
AnnotateTypelibRegistrationForHive(JSONWriter & aJson,HKEY aHive,const nsAString & aTypelibId,const nsAString & aTypelibVersion,const JSONWriter::CollectionStyle aStyle)220 static void AnnotateTypelibRegistrationForHive(
221     JSONWriter& aJson, HKEY aHive, const nsAString& aTypelibId,
222     const nsAString& aTypelibVersion,
223     const JSONWriter::CollectionStyle aStyle) {
224   nsAutoString typelibSubKey;
225   typelibSubKey.AppendLiteral(kSoftwareClasses);
226   typelibSubKey.AppendLiteral(kTypeLib);
227   typelibSubKey.AppendLiteral(kBackslash);
228   typelibSubKey.Append(aTypelibId);
229   typelibSubKey.AppendLiteral(kBackslash);
230   typelibSubKey.Append(aTypelibVersion);
231 
232   nsAutoString typelibDesc;
233   if (GetStringValue(aHive, typelibSubKey, kDefaultValue, typelibDesc)) {
234     aJson.StringProperty("Description", NS_ConvertUTF16toUTF8(typelibDesc));
235   }
236 
237   nsAutoString flagsSubKey(typelibSubKey);
238   flagsSubKey.AppendLiteral(kBackslash);
239   flagsSubKey.AppendLiteral(kFlags);
240 
241   nsAutoString typelibFlags;
242   if (GetStringValue(aHive, flagsSubKey, kDefaultValue, typelibFlags)) {
243     aJson.StringProperty("Flags", NS_ConvertUTF16toUTF8(typelibFlags));
244   }
245 
246   HKEY rawTypelibKey;
247   LONG result =
248       RegOpenKeyEx(aHive, typelibSubKey.get(), 0, KEY_READ, &rawTypelibKey);
249   if (result != ERROR_SUCCESS) {
250     return;
251   }
252   nsAutoRegKey typelibKey(rawTypelibKey);
253 
254   const size_t kMaxLcidCharLen = 9;
255   WCHAR keyName[kMaxLcidCharLen];
256 
257   for (DWORD index = 0; result == ERROR_SUCCESS; ++index) {
258     DWORD keyNameLength = ArrayLength(keyName);
259     result = RegEnumKeyEx(typelibKey, index, keyName, &keyNameLength, nullptr,
260                           nullptr, nullptr, nullptr);
261 
262     unsigned long lcid;
263     if (result == ERROR_SUCCESS && ConvertLCID(keyName, WrapNotNull(&lcid))) {
264       nsDependentString strLcid(keyName, keyNameLength);
265       aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(strLcid), aStyle);
266       AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin32, aStyle);
267 #if defined(HAVE_64BIT_BUILD)
268       AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin64, aStyle);
269 #endif
270       aJson.EndObject();
271     }
272   }
273 }
274 
AnnotateInterfaceRegistrationForHive(JSONWriter & aJson,HKEY aHive,REFIID aIid,const JSONWriter::CollectionStyle aStyle)275 static void AnnotateInterfaceRegistrationForHive(
276     JSONWriter& aJson, HKEY aHive, REFIID aIid,
277     const JSONWriter::CollectionStyle aStyle) {
278   nsAutoString interfaceSubKey;
279   interfaceSubKey.AppendLiteral(kSoftwareClasses);
280   interfaceSubKey.AppendLiteral(kInterface);
281   nsAutoString iid;
282   GUIDToString(aIid, iid);
283   interfaceSubKey.Append(iid);
284 
285   nsAutoString interfaceName;
286   if (GetStringValue(aHive, interfaceSubKey, kDefaultValue, interfaceName)) {
287     aJson.StringProperty("InterfaceName", NS_ConvertUTF16toUTF8(interfaceName));
288   }
289 
290   nsAutoString psSubKey(interfaceSubKey);
291   psSubKey.AppendLiteral(kProxyStubClsid32);
292 
293   nsAutoString psClsid;
294   if (GetStringValue(aHive, psSubKey, kDefaultValue, psClsid)) {
295     aJson.StartObjectProperty("ProxyStub", aStyle);
296     aJson.StringProperty("CLSID", NS_ConvertUTF16toUTF8(psClsid));
297     AnnotateClsidRegistrationForHive(aJson, aHive, psClsid, aStyle);
298     aJson.EndObject();
299   }
300 
301   nsAutoString typelibSubKey(interfaceSubKey);
302   typelibSubKey.AppendLiteral(kTypeLib);
303 
304   nsAutoString typelibId;
305   bool haveTypelibId =
306       GetStringValue(aHive, typelibSubKey, kDefaultValue, typelibId);
307 
308   nsAutoString typelibVersion;
309   bool haveTypelibVersion =
310       GetStringValue(aHive, typelibSubKey, kVersion, typelibVersion);
311 
312   if (haveTypelibId || haveTypelibVersion) {
313     aJson.StartObjectProperty("TypeLib", aStyle);
314   }
315 
316   if (haveTypelibId) {
317     aJson.StringProperty("ID", NS_ConvertUTF16toUTF8(typelibId));
318   }
319 
320   if (haveTypelibVersion) {
321     aJson.StringProperty("Version", NS_ConvertUTF16toUTF8(typelibVersion));
322   }
323 
324   if (haveTypelibId && haveTypelibVersion) {
325     AnnotateTypelibRegistrationForHive(aJson, aHive, typelibId, typelibVersion,
326                                        aStyle);
327   }
328 
329   if (haveTypelibId || haveTypelibVersion) {
330     aJson.EndObject();
331   }
332 }
333 
AnnotateInterfaceRegistration(REFIID aIid)334 void AnnotateInterfaceRegistration(REFIID aIid) {
335 #if defined(DEBUG)
336   const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
337 #else
338   const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
339 #endif
340 
341   JSONWriter json(MakeUnique<CStringWriter>());
342 
343   json.Start(style);
344 
345   json.StartObjectProperty("HKLM", style);
346   AnnotateInterfaceRegistrationForHive(json, HKEY_LOCAL_MACHINE, aIid, style);
347   json.EndObject();
348 
349   json.StartObjectProperty("HKCU", style);
350   AnnotateInterfaceRegistrationForHive(json, HKEY_CURRENT_USER, aIid, style);
351   json.EndObject();
352 
353   json.End();
354 
355   CrashReporter::Annotation annotationKey;
356   if (XRE_IsParentProcess()) {
357     annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoParent;
358   } else {
359     annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoChild;
360   }
361   CrashReporter::AnnotateCrashReport(
362       annotationKey, static_cast<CStringWriter*>(json.WriteFunc())->Get());
363 }
364 
AnnotateClassRegistration(REFCLSID aClsid)365 void AnnotateClassRegistration(REFCLSID aClsid) {
366 #if defined(DEBUG)
367   const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
368 #else
369   const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
370 #endif
371 
372   nsAutoString strClsid;
373   GUIDToString(aClsid, strClsid);
374 
375   JSONWriter json(MakeUnique<CStringWriter>());
376 
377   json.Start(style);
378 
379   json.StartObjectProperty("HKLM", style);
380   AnnotateClsidRegistrationForHive(json, HKEY_LOCAL_MACHINE, strClsid, style);
381   json.EndObject();
382 
383   json.StartObjectProperty("HKCU", style);
384   AnnotateClsidRegistrationForHive(json, HKEY_CURRENT_USER, strClsid, style);
385   json.EndObject();
386 
387   json.End();
388 
389   CrashReporter::Annotation annotationKey;
390   if (XRE_IsParentProcess()) {
391     annotationKey = CrashReporter::Annotation::ClassRegistrationInfoParent;
392   } else {
393     annotationKey = CrashReporter::Annotation::ClassRegistrationInfoChild;
394   }
395 
396   CrashReporter::AnnotateCrashReport(
397       annotationKey, static_cast<CStringWriter*>(json.WriteFunc())->Get());
398 }
399 
400 }  // namespace mscom
401 }  // namespace mozilla
402