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 "mozilla/mscom/MainThreadRuntime.h"
8 
9 #if defined(ACCESSIBILITY)
10 #include "mozilla/a11y/Compatibility.h"
11 #endif
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/RefPtr.h"
15 #include "mozilla/UniquePtr.h"
16 #if defined(ACCESSIBILITY)
17 #include "nsExceptionHandler.h"
18 #endif  // defined(ACCESSIBILITY)
19 #include "nsWindowsHelpers.h"
20 #include "nsXULAppAPI.h"
21 
22 #include <accctrl.h>
23 #include <aclapi.h>
24 #include <objbase.h>
25 #include <objidl.h>
26 
27 namespace {
28 
29 struct LocalFreeDeleter {
operator ()__anond99d968f0111::LocalFreeDeleter30   void operator()(void* aPtr) { ::LocalFree(aPtr); }
31 };
32 
33 }  // anonymous namespace
34 
35 // This API from oleaut32.dll is not declared in Windows SDK headers
36 extern "C" void __cdecl SetOaNoCache(void);
37 
38 namespace mozilla {
39 namespace mscom {
40 
41 MainThreadRuntime* MainThreadRuntime::sInstance = nullptr;
42 
MainThreadRuntime()43 MainThreadRuntime::MainThreadRuntime()
44     : mInitResult(E_UNEXPECTED)
45 #if defined(ACCESSIBILITY)
46       ,
47       mActCtxRgn(a11y::Compatibility::GetActCtxResourceId())
48 #endif  // defined(ACCESSIBILITY)
49 {
50   // We must be the outermost COM initialization on this thread. The COM runtime
51   // cannot be configured once we start manipulating objects
52   MOZ_ASSERT(mStaRegion.IsValidOutermost());
53   if (NS_WARN_IF(!mStaRegion.IsValidOutermost())) {
54     return;
55   }
56 
57   // We are required to initialize security in order to configure global
58   // options.
59   mInitResult = InitializeSecurity();
60   MOZ_ASSERT(SUCCEEDED(mInitResult));
61   if (FAILED(mInitResult)) {
62     return;
63   }
64 
65   RefPtr<IGlobalOptions> globalOpts;
66   mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
67                                    CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
68                                    (void**)getter_AddRefs(globalOpts));
69   MOZ_ASSERT(SUCCEEDED(mInitResult));
70   if (FAILED(mInitResult)) {
71     return;
72   }
73 
74   // Disable COM's catch-all exception handler
75   mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING,
76                                 COMGLB_EXCEPTION_DONOT_HANDLE_ANY);
77   MOZ_ASSERT(SUCCEEDED(mInitResult));
78 
79   // Disable the BSTR cache (as it never invalidates, thus leaking memory)
80   ::SetOaNoCache();
81 
82   if (FAILED(mInitResult)) {
83     return;
84   }
85 
86   if (XRE_IsParentProcess()) {
87     MainThreadClientInfo::Create(getter_AddRefs(mClientInfo));
88   }
89 
90   MOZ_ASSERT(!sInstance);
91   sInstance = this;
92 }
93 
~MainThreadRuntime()94 MainThreadRuntime::~MainThreadRuntime() {
95   if (mClientInfo) {
96     mClientInfo->Detach();
97   }
98 
99   MOZ_ASSERT(sInstance == this);
100   if (sInstance == this) {
101     sInstance = nullptr;
102   }
103 }
104 
105 /* static */
106 DWORD
GetClientThreadId()107 MainThreadRuntime::GetClientThreadId() {
108   MOZ_ASSERT(NS_IsMainThread());
109   MOZ_ASSERT(XRE_IsParentProcess(), "Unsupported outside of parent process");
110   if (!XRE_IsParentProcess()) {
111     return 0;
112   }
113 
114   // Don't check for a calling executable if the caller is in-process.
115   // We verify this by asking COM for a call context. If none exists, then
116   // we must be a local call.
117   RefPtr<IServerSecurity> serverSecurity;
118   if (FAILED(::CoGetCallContext(IID_IServerSecurity,
119                                 getter_AddRefs(serverSecurity)))) {
120     return 0;
121   }
122 
123   MOZ_ASSERT(sInstance);
124   if (!sInstance) {
125     return 0;
126   }
127 
128   MOZ_ASSERT(sInstance->mClientInfo);
129   if (!sInstance->mClientInfo) {
130     return 0;
131   }
132 
133   return sInstance->mClientInfo->GetLastRemoteCallThreadId();
134 }
135 
136 HRESULT
InitializeSecurity()137 MainThreadRuntime::InitializeSecurity() {
138   HANDLE rawToken = nullptr;
139   BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
140   if (!ok) {
141     return HRESULT_FROM_WIN32(::GetLastError());
142   }
143   nsAutoHandle token(rawToken);
144 
145   DWORD len = 0;
146   ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
147   DWORD win32Error = ::GetLastError();
148   if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
149     return HRESULT_FROM_WIN32(win32Error);
150   }
151 
152   auto tokenUserBuf = MakeUnique<BYTE[]>(len);
153   TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
154   ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
155   if (!ok) {
156     return HRESULT_FROM_WIN32(::GetLastError());
157   }
158 
159   len = 0;
160   ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
161   win32Error = ::GetLastError();
162   if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
163     return HRESULT_FROM_WIN32(win32Error);
164   }
165 
166   auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
167   TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
168       *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
169   ok = ::GetTokenInformation(token, TokenPrimaryGroup,
170                              tokenPrimaryGroupBuf.get(), len, &len);
171   if (!ok) {
172     return HRESULT_FROM_WIN32(::GetLastError());
173   }
174 
175   SECURITY_DESCRIPTOR sd;
176   if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
177     return HRESULT_FROM_WIN32(::GetLastError());
178   }
179 
180   BYTE systemSid[SECURITY_MAX_SID_SIZE];
181   DWORD systemSidSize = sizeof(systemSid);
182   if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
183                             &systemSidSize)) {
184     return HRESULT_FROM_WIN32(::GetLastError());
185   }
186 
187   BYTE adminSid[SECURITY_MAX_SID_SIZE];
188   DWORD adminSidSize = sizeof(adminSid);
189   if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
190                             &adminSidSize)) {
191     return HRESULT_FROM_WIN32(::GetLastError());
192   }
193 
194   // Grant access to SYSTEM, Administrators, and the user.
195   EXPLICIT_ACCESS entries[] = {
196       {COM_RIGHTS_EXECUTE,
197        GRANT_ACCESS,
198        NO_INHERITANCE,
199        {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
200         reinterpret_cast<LPWSTR>(systemSid)}},
201       {COM_RIGHTS_EXECUTE,
202        GRANT_ACCESS,
203        NO_INHERITANCE,
204        {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
205         TRUSTEE_IS_WELL_KNOWN_GROUP, reinterpret_cast<LPWSTR>(adminSid)}},
206       {COM_RIGHTS_EXECUTE,
207        GRANT_ACCESS,
208        NO_INHERITANCE,
209        {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
210         reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}};
211 
212   PACL rawDacl = nullptr;
213   win32Error =
214       ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr, &rawDacl);
215   if (win32Error != ERROR_SUCCESS) {
216     return HRESULT_FROM_WIN32(win32Error);
217   }
218 
219   UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
220 
221   if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
222     return HRESULT_FROM_WIN32(::GetLastError());
223   }
224 
225   if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
226     return HRESULT_FROM_WIN32(::GetLastError());
227   }
228 
229   if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup,
230                                     FALSE)) {
231     return HRESULT_FROM_WIN32(::GetLastError());
232   }
233 
234   return ::CoInitializeSecurity(
235       &sd, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT,
236       RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE, nullptr);
237 }
238 
239 }  // namespace mscom
240 }  // namespace mozilla
241