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