Backed out changeset f85447f6f56d (bug 1891145) for causing mochitest failures @...
[gecko.git] / ipc / mscom / ProcessRuntime.cpp
blobae4e32247c80ff195f7bf5aa27368e05400e50c9
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/. */
7 #include "mozilla/mscom/ProcessRuntime.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
11 #include "mozilla/mscom/COMWrappers.h"
12 #include "mozilla/mscom/ProcessRuntimeShared.h"
13 #include "mozilla/RefPtr.h"
14 #include "mozilla/UniquePtr.h"
15 #include "mozilla/Unused.h"
16 #include "mozilla/Vector.h"
17 #include "mozilla/WindowsProcessMitigations.h"
19 #if defined(MOZILLA_INTERNAL_API)
20 # include "mozilla/mscom/EnsureMTA.h"
21 # if defined(MOZ_SANDBOX)
22 # include "mozilla/sandboxTarget.h"
23 # endif // defined(MOZ_SANDBOX)
24 #endif // defined(MOZILLA_INTERNAL_API)
26 #include <accctrl.h>
27 #include <aclapi.h>
28 #include <objbase.h>
29 #include <objidl.h>
31 // This API from oleaut32.dll is not declared in Windows SDK headers
32 extern "C" void __cdecl SetOaNoCache(void);
34 using namespace mozilla::mscom::detail;
36 namespace mozilla {
37 namespace mscom {
39 #if defined(MOZILLA_INTERNAL_API)
40 ProcessRuntime* ProcessRuntime::sInstance = nullptr;
42 ProcessRuntime::ProcessRuntime() : ProcessRuntime(XRE_GetProcessType()) {}
44 ProcessRuntime::ProcessRuntime(const GeckoProcessType aProcessType)
45 : ProcessRuntime(aProcessType == GeckoProcessType_Default
46 ? ProcessCategory::GeckoBrowserParent
47 : ProcessCategory::GeckoChild) {}
48 #endif // defined(MOZILLA_INTERNAL_API)
50 ProcessRuntime::ProcessRuntime(const ProcessCategory aProcessCategory)
51 : mInitResult(CO_E_NOTINITIALIZED), mProcessCategory(aProcessCategory) {
52 #if defined(MOZILLA_INTERNAL_API)
53 MOZ_DIAGNOSTIC_ASSERT(!sInstance);
54 sInstance = this;
56 EnsureMTA();
57 /**
58 * From this point forward, all threads in this process are implicitly
59 * members of the multi-threaded apartment, with the following exceptions:
60 * 1. If any Win32 GUI APIs were called on the current thread prior to
61 * executing this constructor, then this thread has already been implicitly
62 * initialized as the process's main STA thread; or
63 * 2. A thread explicitly and successfully calls CoInitialize(Ex) to specify
64 * otherwise.
67 const bool isCurThreadImplicitMTA = IsCurrentThreadImplicitMTA();
68 // We only assert that the implicit MTA precondition holds when not running
69 // as the Gecko parent process.
70 MOZ_DIAGNOSTIC_ASSERT(aProcessCategory ==
71 ProcessCategory::GeckoBrowserParent ||
72 isCurThreadImplicitMTA);
74 # if defined(MOZ_SANDBOX)
75 const bool isLockedDownChildProcess =
76 mProcessCategory == ProcessCategory::GeckoChild && IsWin32kLockedDown();
77 // If our process is running under Win32k lockdown, we cannot initialize
78 // COM with a single-threaded apartment. This is because STAs create a hidden
79 // window, which implicitly requires user32 and Win32k, which are blocked.
80 // Instead we start the multi-threaded apartment and conduct our process-wide
81 // COM initialization there.
82 if (isLockedDownChildProcess) {
83 // Make sure we're still running with the sandbox's privileged impersonation
84 // token.
85 HANDLE rawCurThreadImpToken;
86 if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY,
87 FALSE, &rawCurThreadImpToken)) {
88 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
89 return;
91 nsAutoHandle curThreadImpToken(rawCurThreadImpToken);
93 // Ensure that our current token is still an impersonation token (ie, we
94 // have not yet called RevertToSelf() on this thread).
95 DWORD len;
96 TOKEN_TYPE tokenType;
97 MOZ_RELEASE_ASSERT(
98 ::GetTokenInformation(rawCurThreadImpToken, TokenType, &tokenType,
99 sizeof(tokenType), &len) &&
100 len == sizeof(tokenType) && tokenType == TokenImpersonation);
102 // Ideally we want our current thread to be running implicitly inside the
103 // MTA, but if for some wacky reason we did not end up with that, we may
104 // compensate by completing initialization via EnsureMTA's persistent
105 // thread.
106 if (!isCurThreadImplicitMTA) {
107 InitUsingPersistentMTAThread(curThreadImpToken);
108 return;
111 # endif // defined(MOZ_SANDBOX)
112 #endif // defined(MOZILLA_INTERNAL_API)
114 mAptRegion.Init(GetDesiredApartmentType(mProcessCategory));
116 // It can happen that we are not the outermost COM initialization on this
117 // thread. In fact it should regularly be the case that the outermost
118 // initialization occurs from outside of XUL, before we show the skeleton UI,
119 // at which point we still need to run some things here from within XUL.
120 if (!mAptRegion.IsValidOutermost()) {
121 mInitResult = mAptRegion.GetHResult();
122 #if defined(MOZILLA_INTERNAL_API)
123 MOZ_ASSERT(mProcessCategory == ProcessCategory::GeckoBrowserParent);
124 if (mProcessCategory != ProcessCategory::GeckoBrowserParent) {
125 // This is unexpected unless we're GeckoBrowserParent
126 return;
129 ProcessInitLock lock;
131 // Is another instance of ProcessRuntime responsible for the outer
132 // initialization?
133 const bool prevInit =
134 lock.GetInitState() == ProcessInitState::FullyInitialized;
135 MOZ_ASSERT(prevInit);
136 if (prevInit) {
137 PostInit();
139 #endif // defined(MOZILLA_INTERNAL_API)
140 return;
143 InitInsideApartment();
144 if (FAILED(mInitResult)) {
145 return;
148 #if defined(MOZILLA_INTERNAL_API)
149 # if defined(MOZ_SANDBOX)
150 if (isLockedDownChildProcess) {
151 // In locked-down child processes, defer PostInit until priv drop
152 SandboxTarget::Instance()->RegisterSandboxStartCallback([self = this]() {
153 // Ensure that we're still live and the init was successful before
154 // calling PostInit()
155 if (self == sInstance && SUCCEEDED(self->mInitResult)) {
156 PostInit();
159 return;
161 # endif // defined(MOZ_SANDBOX)
163 PostInit();
164 #endif // defined(MOZILLA_INTERNAL_API)
167 #if defined(MOZILLA_INTERNAL_API)
168 ProcessRuntime::~ProcessRuntime() {
169 MOZ_DIAGNOSTIC_ASSERT(sInstance == this);
170 sInstance = nullptr;
173 # if defined(MOZ_SANDBOX)
174 void ProcessRuntime::InitUsingPersistentMTAThread(
175 const nsAutoHandle& aCurThreadToken) {
176 // Create an impersonation token based on the current thread's token
177 HANDLE rawMtaThreadImpToken = nullptr;
178 if (!::DuplicateToken(aCurThreadToken, SecurityImpersonation,
179 &rawMtaThreadImpToken)) {
180 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
181 return;
183 nsAutoHandle mtaThreadImpToken(rawMtaThreadImpToken);
185 // Impersonate and initialize.
186 bool tokenSet = false;
187 EnsureMTA(
188 [this, rawMtaThreadImpToken, &tokenSet]() -> void {
189 if (!::SetThreadToken(nullptr, rawMtaThreadImpToken)) {
190 mInitResult = HRESULT_FROM_WIN32(::GetLastError());
191 return;
194 tokenSet = true;
195 InitInsideApartment();
197 EnsureMTA::Option::ForceDispatchToPersistentThread);
199 if (!tokenSet) {
200 return;
203 SandboxTarget::Instance()->RegisterSandboxStartCallback(
204 [self = this]() -> void {
205 EnsureMTA(
206 []() -> void {
207 // This is a security risk if it fails, so we release assert
208 MOZ_RELEASE_ASSERT(::RevertToSelf(),
209 "mscom::ProcessRuntime RevertToSelf failed");
211 EnsureMTA::Option::ForceDispatchToPersistentThread);
213 // Ensure that we're still live and the init was successful before
214 // calling PostInit()
215 if (self == sInstance && SUCCEEDED(self->mInitResult)) {
216 PostInit();
220 # endif // defined(MOZ_SANDBOX)
221 #endif // defined(MOZILLA_INTERNAL_API)
223 /* static */
224 COINIT ProcessRuntime::GetDesiredApartmentType(
225 const ProcessRuntime::ProcessCategory aProcessCategory) {
226 switch (aProcessCategory) {
227 case ProcessCategory::GeckoBrowserParent:
228 return COINIT_APARTMENTTHREADED;
229 case ProcessCategory::GeckoChild:
230 if (!IsWin32kLockedDown()) {
231 // If Win32k is not locked down then we probably still need STA.
232 // We disable DDE since that is not usable from child processes.
233 return static_cast<COINIT>(COINIT_APARTMENTTHREADED |
234 COINIT_DISABLE_OLE1DDE);
237 [[fallthrough]];
238 default:
239 return COINIT_MULTITHREADED;
243 void ProcessRuntime::InitInsideApartment() {
244 ProcessInitLock lock;
245 const ProcessInitState prevInitState = lock.GetInitState();
246 if (prevInitState == ProcessInitState::FullyInitialized) {
247 // COM has already been initialized by a previous ProcessRuntime instance
248 mInitResult = S_OK;
249 return;
252 if (prevInitState < ProcessInitState::PartialSecurityInitialized) {
253 // We are required to initialize security prior to configuring global
254 // options.
255 mInitResult = InitializeSecurity(mProcessCategory);
256 MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(mInitResult));
258 // Even though this isn't great, we should try to proceed even when
259 // CoInitializeSecurity has previously been called: the additional settings
260 // we want to change are important enough that we don't want to skip them.
261 if (FAILED(mInitResult) && mInitResult != RPC_E_TOO_LATE) {
262 return;
265 lock.SetInitState(ProcessInitState::PartialSecurityInitialized);
268 if (prevInitState < ProcessInitState::PartialGlobalOptions) {
269 RefPtr<IGlobalOptions> globalOpts;
270 mInitResult = wrapped::CoCreateInstance(
271 CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
272 getter_AddRefs(globalOpts));
273 MOZ_ASSERT(SUCCEEDED(mInitResult));
274 if (FAILED(mInitResult)) {
275 return;
278 // Disable COM's catch-all exception handler
279 mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING,
280 COMGLB_EXCEPTION_DONOT_HANDLE_ANY);
281 MOZ_ASSERT(SUCCEEDED(mInitResult));
282 if (FAILED(mInitResult)) {
283 return;
286 lock.SetInitState(ProcessInitState::PartialGlobalOptions);
289 // Disable the BSTR cache (as it never invalidates, thus leaking memory)
290 // (This function is itself idempotent, so we do not concern ourselves with
291 // tracking whether or not we've already called it.)
292 ::SetOaNoCache();
294 lock.SetInitState(ProcessInitState::FullyInitialized);
297 #if defined(MOZILLA_INTERNAL_API)
299 * Guaranteed to run *after* the COM (and possible sandboxing) initialization
300 * has successfully completed and stabilized. This method MUST BE IDEMPOTENT!
302 /* static */ void ProcessRuntime::PostInit() {
303 // Currently "roughed-in" but unused.
305 #endif // defined(MOZILLA_INTERNAL_API)
307 /* static */
308 DWORD
309 ProcessRuntime::GetClientThreadId() {
310 DWORD callerTid;
311 HRESULT hr = ::CoGetCallerTID(&callerTid);
312 // Don't return callerTid unless the call succeeded and returned S_FALSE,
313 // indicating that the caller originates from a different process.
314 if (hr != S_FALSE) {
315 return 0;
318 return callerTid;
321 /* static */
322 HRESULT
323 ProcessRuntime::InitializeSecurity(const ProcessCategory aProcessCategory) {
324 HANDLE rawToken = nullptr;
325 BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
326 if (!ok) {
327 return HRESULT_FROM_WIN32(::GetLastError());
329 nsAutoHandle token(rawToken);
331 DWORD len = 0;
332 ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
333 DWORD win32Error = ::GetLastError();
334 if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
335 return HRESULT_FROM_WIN32(win32Error);
338 auto tokenUserBuf = MakeUnique<BYTE[]>(len);
339 TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
340 ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
341 if (!ok) {
342 return HRESULT_FROM_WIN32(::GetLastError());
345 len = 0;
346 ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
347 win32Error = ::GetLastError();
348 if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
349 return HRESULT_FROM_WIN32(win32Error);
352 auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
353 TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
354 *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
355 ok = ::GetTokenInformation(token, TokenPrimaryGroup,
356 tokenPrimaryGroupBuf.get(), len, &len);
357 if (!ok) {
358 return HRESULT_FROM_WIN32(::GetLastError());
361 SECURITY_DESCRIPTOR sd;
362 if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
363 return HRESULT_FROM_WIN32(::GetLastError());
366 BYTE systemSid[SECURITY_MAX_SID_SIZE];
367 DWORD systemSidSize = sizeof(systemSid);
368 if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
369 &systemSidSize)) {
370 return HRESULT_FROM_WIN32(::GetLastError());
373 BYTE adminSid[SECURITY_MAX_SID_SIZE];
374 DWORD adminSidSize = sizeof(adminSid);
375 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
376 &adminSidSize)) {
377 return HRESULT_FROM_WIN32(::GetLastError());
380 const bool allowAllNonRestrictedAppContainers =
381 aProcessCategory == ProcessCategory::GeckoBrowserParent;
383 BYTE appContainersSid[SECURITY_MAX_SID_SIZE];
384 DWORD appContainersSidSize = sizeof(appContainersSid);
385 if (allowAllNonRestrictedAppContainers) {
386 if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid, nullptr,
387 appContainersSid, &appContainersSidSize)) {
388 return HRESULT_FROM_WIN32(::GetLastError());
392 UniquePtr<BYTE[]> tokenAppContainerInfBuf;
393 len = 0;
394 ::GetTokenInformation(token, TokenAppContainerSid, nullptr, len, &len);
395 if (len) {
396 tokenAppContainerInfBuf = MakeUnique<BYTE[]>(len);
397 ok = ::GetTokenInformation(token, TokenAppContainerSid,
398 tokenAppContainerInfBuf.get(), len, &len);
399 if (!ok) {
400 // Don't fail if we get an error retrieving an app container SID.
401 tokenAppContainerInfBuf = nullptr;
405 // Grant access to SYSTEM, Administrators, the user, our app container (if in
406 // one) and when running as the browser process on Windows 8+, all non
407 // restricted app containers.
408 const size_t kMaxInlineEntries = 5;
409 mozilla::Vector<EXPLICIT_ACCESS_W, kMaxInlineEntries> entries;
411 Unused << entries.append(EXPLICIT_ACCESS_W{
412 COM_RIGHTS_EXECUTE,
413 GRANT_ACCESS,
414 NO_INHERITANCE,
415 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
416 reinterpret_cast<LPWSTR>(systemSid)}});
418 Unused << entries.append(EXPLICIT_ACCESS_W{
419 COM_RIGHTS_EXECUTE,
420 GRANT_ACCESS,
421 NO_INHERITANCE,
422 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
423 TRUSTEE_IS_WELL_KNOWN_GROUP, reinterpret_cast<LPWSTR>(adminSid)}});
425 Unused << entries.append(EXPLICIT_ACCESS_W{
426 COM_RIGHTS_EXECUTE,
427 GRANT_ACCESS,
428 NO_INHERITANCE,
429 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
430 reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}});
432 if (allowAllNonRestrictedAppContainers) {
433 Unused << entries.append(
434 EXPLICIT_ACCESS_W{COM_RIGHTS_EXECUTE,
435 GRANT_ACCESS,
436 NO_INHERITANCE,
437 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID,
438 TRUSTEE_IS_WELL_KNOWN_GROUP,
439 reinterpret_cast<LPWSTR>(appContainersSid)}});
442 if (tokenAppContainerInfBuf) {
443 TOKEN_APPCONTAINER_INFORMATION& tokenAppContainerInf =
444 *reinterpret_cast<TOKEN_APPCONTAINER_INFORMATION*>(
445 tokenAppContainerInfBuf.get());
447 // TokenAppContainer will be null if we are not in an app container.
448 if (tokenAppContainerInf.TokenAppContainer) {
449 Unused << entries.append(EXPLICIT_ACCESS_W{
450 COM_RIGHTS_EXECUTE,
451 GRANT_ACCESS,
452 NO_INHERITANCE,
453 {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
454 reinterpret_cast<LPWSTR>(tokenAppContainerInf.TokenAppContainer)}});
458 PACL rawDacl = nullptr;
459 win32Error =
460 ::SetEntriesInAclW(entries.length(), entries.begin(), nullptr, &rawDacl);
461 if (win32Error != ERROR_SUCCESS) {
462 return HRESULT_FROM_WIN32(win32Error);
465 UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
467 if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
468 return HRESULT_FROM_WIN32(::GetLastError());
471 if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
472 return HRESULT_FROM_WIN32(::GetLastError());
475 if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup,
476 FALSE)) {
477 return HRESULT_FROM_WIN32(::GetLastError());
480 return wrapped::CoInitializeSecurity(
481 &sd, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT,
482 RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE, nullptr);
485 } // namespace mscom
486 } // namespace mozilla