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 #if defined(ACCESSIBILITY) && \
10 (defined(MOZILLA_INTERNAL_API) || defined(MOZ_HAS_MOZGLUE))
11 # include "mozilla/mscom/ActCtxResource.h"
12 #endif // defined(ACCESSIBILITY) && (defined(MOZILLA_INTERNAL_API) ||
13 // defined(MOZ_HAS_MOZGLUE))
14 #include "mozilla/Assertions.h"
15 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
16 #include "mozilla/mscom/COMWrappers.h"
17 #include "mozilla/mscom/ProcessRuntimeShared.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/Unused.h"
21 #include "mozilla/Vector.h"
22 #include "mozilla/WindowsProcessMitigations.h"
23 #include "mozilla/WindowsVersion.h"
25 #if defined(MOZILLA_INTERNAL_API)
26 # include "mozilla/mscom/EnsureMTA.h"
27 # if defined(MOZ_SANDBOX)
28 # include "mozilla/sandboxTarget.h"
29 # endif // defined(MOZ_SANDBOX)
30 #endif // defined(MOZILLA_INTERNAL_API)
37 // This API from oleaut32.dll is not declared in Windows SDK headers
38 extern "C" void __cdecl
SetOaNoCache(void);
40 using namespace mozilla::mscom::detail
;
45 #if defined(MOZILLA_INTERNAL_API)
46 ProcessRuntime
* ProcessRuntime::sInstance
= nullptr;
48 ProcessRuntime::ProcessRuntime() : ProcessRuntime(XRE_GetProcessType()) {}
50 ProcessRuntime::ProcessRuntime(const GeckoProcessType aProcessType
)
51 : ProcessRuntime(aProcessType
== GeckoProcessType_Default
52 ? ProcessCategory::GeckoBrowserParent
53 : ProcessCategory::GeckoChild
) {}
54 #endif // defined(MOZILLA_INTERNAL_API)
56 ProcessRuntime::ProcessRuntime(const ProcessCategory aProcessCategory
)
57 : mInitResult(CO_E_NOTINITIALIZED
), mProcessCategory(aProcessCategory
) {
58 #if defined(ACCESSIBILITY)
59 # if defined(MOZILLA_INTERNAL_API)
60 // If we're inside XUL, and we're the parent process, then we trust that
61 // this has already been initialized for us prior to XUL being loaded.
62 // Only required in the child if the Resource ID has been passed down.
63 if (aProcessCategory
!= ProcessCategory::GeckoBrowserParent
&&
64 ActCtxResource::GetAccessibilityResourceId()) {
65 mActCtxRgn
.emplace(ActCtxResource::GetAccessibilityResource());
67 # elif defined(MOZ_HAS_MOZGLUE)
68 // If we're here, then we're in mozglue and initializing this for the parent
70 MOZ_ASSERT(aProcessCategory
== ProcessCategory::GeckoBrowserParent
);
71 mActCtxRgn
.emplace(ActCtxResource::GetAccessibilityResource());
73 #endif // defined(ACCESSIBILITY)
75 #if defined(MOZILLA_INTERNAL_API)
76 MOZ_DIAGNOSTIC_ASSERT(!sInstance
);
81 * From this point forward, all threads in this process are implicitly
82 * members of the multi-threaded apartment, with the following exceptions:
83 * 1. If any Win32 GUI APIs were called on the current thread prior to
84 * executing this constructor, then this thread has already been implicitly
85 * initialized as the process's main STA thread; or
86 * 2. A thread explicitly and successfully calls CoInitialize(Ex) to specify
90 const bool isCurThreadImplicitMTA
= IsCurrentThreadImplicitMTA();
91 // We only assert that the implicit MTA precondition holds when not running
92 // as the Gecko parent process.
93 MOZ_DIAGNOSTIC_ASSERT(aProcessCategory
==
94 ProcessCategory::GeckoBrowserParent
||
95 isCurThreadImplicitMTA
);
97 # if defined(MOZ_SANDBOX)
98 const bool isLockedDownChildProcess
=
99 mProcessCategory
== ProcessCategory::GeckoChild
&& IsWin32kLockedDown();
100 // If our process is running under Win32k lockdown, we cannot initialize
101 // COM with a single-threaded apartment. This is because STAs create a hidden
102 // window, which implicitly requires user32 and Win32k, which are blocked.
103 // Instead we start the multi-threaded apartment and conduct our process-wide
104 // COM initialization there.
105 if (isLockedDownChildProcess
) {
106 // Make sure we're still running with the sandbox's privileged impersonation
108 HANDLE rawCurThreadImpToken
;
109 if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_DUPLICATE
| TOKEN_QUERY
,
110 FALSE
, &rawCurThreadImpToken
)) {
111 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
114 nsAutoHandle
curThreadImpToken(rawCurThreadImpToken
);
116 // Ensure that our current token is still an impersonation token (ie, we
117 // have not yet called RevertToSelf() on this thread).
119 TOKEN_TYPE tokenType
;
121 ::GetTokenInformation(rawCurThreadImpToken
, TokenType
, &tokenType
,
122 sizeof(tokenType
), &len
) &&
123 len
== sizeof(tokenType
) && tokenType
== TokenImpersonation
);
125 // Ideally we want our current thread to be running implicitly inside the
126 // MTA, but if for some wacky reason we did not end up with that, we may
127 // compensate by completing initialization via EnsureMTA's persistent
129 if (!isCurThreadImplicitMTA
) {
130 InitUsingPersistentMTAThread(curThreadImpToken
);
134 # endif // defined(MOZ_SANDBOX)
135 #endif // defined(MOZILLA_INTERNAL_API)
137 mAptRegion
.Init(GetDesiredApartmentType(mProcessCategory
));
139 // It can happen that we are not the outermost COM initialization on this
140 // thread. In fact it should regularly be the case that the outermost
141 // initialization occurs from outside of XUL, before we show the skeleton UI,
142 // at which point we still need to run some things here from within XUL.
143 if (!mAptRegion
.IsValidOutermost()) {
144 mInitResult
= mAptRegion
.GetHResult();
145 #if defined(MOZILLA_INTERNAL_API)
146 MOZ_ASSERT(mProcessCategory
== ProcessCategory::GeckoBrowserParent
);
147 if (mProcessCategory
!= ProcessCategory::GeckoBrowserParent
) {
148 // This is unexpected unless we're GeckoBrowserParent
152 ProcessInitLock lock
;
154 // Is another instance of ProcessRuntime responsible for the outer
156 const bool prevInit
=
157 lock
.GetInitState() == ProcessInitState::FullyInitialized
;
158 MOZ_ASSERT(prevInit
);
162 #endif // defined(MOZILLA_INTERNAL_API)
166 InitInsideApartment();
167 if (FAILED(mInitResult
)) {
171 #if defined(MOZILLA_INTERNAL_API)
172 # if defined(MOZ_SANDBOX)
173 if (isLockedDownChildProcess
) {
174 // In locked-down child processes, defer PostInit until priv drop
175 SandboxTarget::Instance()->RegisterSandboxStartCallback([self
= this]() {
176 // Ensure that we're still live and the init was successful before
177 // calling PostInit()
178 if (self
== sInstance
&& SUCCEEDED(self
->mInitResult
)) {
184 # endif // defined(MOZ_SANDBOX)
187 #endif // defined(MOZILLA_INTERNAL_API)
190 #if defined(MOZILLA_INTERNAL_API)
191 ProcessRuntime::~ProcessRuntime() {
192 MOZ_DIAGNOSTIC_ASSERT(sInstance
== this);
196 # if defined(MOZ_SANDBOX)
197 void ProcessRuntime::InitUsingPersistentMTAThread(
198 const nsAutoHandle
& aCurThreadToken
) {
199 // Create an impersonation token based on the current thread's token
200 HANDLE rawMtaThreadImpToken
= nullptr;
201 if (!::DuplicateToken(aCurThreadToken
, SecurityImpersonation
,
202 &rawMtaThreadImpToken
)) {
203 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
206 nsAutoHandle
mtaThreadImpToken(rawMtaThreadImpToken
);
208 // Impersonate and initialize.
209 bool tokenSet
= false;
211 [this, rawMtaThreadImpToken
, &tokenSet
]() -> void {
212 if (!::SetThreadToken(nullptr, rawMtaThreadImpToken
)) {
213 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
218 InitInsideApartment();
220 EnsureMTA::Option::ForceDispatchToPersistentThread
);
226 SandboxTarget::Instance()->RegisterSandboxStartCallback(
227 [self
= this]() -> void {
230 // This is a security risk if it fails, so we release assert
231 MOZ_RELEASE_ASSERT(::RevertToSelf(),
232 "mscom::ProcessRuntime RevertToSelf failed");
234 EnsureMTA::Option::ForceDispatchToPersistentThread
);
236 // Ensure that we're still live and the init was successful before
237 // calling PostInit()
238 if (self
== sInstance
&& SUCCEEDED(self
->mInitResult
)) {
243 # endif // defined(MOZ_SANDBOX)
244 #endif // defined(MOZILLA_INTERNAL_API)
247 COINIT
ProcessRuntime::GetDesiredApartmentType(
248 const ProcessRuntime::ProcessCategory aProcessCategory
) {
249 switch (aProcessCategory
) {
250 case ProcessCategory::GeckoBrowserParent
:
251 return COINIT_APARTMENTTHREADED
;
252 case ProcessCategory::GeckoChild
:
253 if (!IsWin32kLockedDown()) {
254 // If Win32k is not locked down then we probably still need STA.
255 // We disable DDE since that is not usable from child processes.
256 return static_cast<COINIT
>(COINIT_APARTMENTTHREADED
|
257 COINIT_DISABLE_OLE1DDE
);
262 return COINIT_MULTITHREADED
;
266 void ProcessRuntime::InitInsideApartment() {
267 ProcessInitLock lock
;
268 const ProcessInitState prevInitState
= lock
.GetInitState();
269 if (prevInitState
== ProcessInitState::FullyInitialized
) {
270 // COM has already been initialized by a previous ProcessRuntime instance
275 if (prevInitState
< ProcessInitState::PartialSecurityInitialized
) {
276 // We are required to initialize security prior to configuring global
278 mInitResult
= InitializeSecurity(mProcessCategory
);
279 MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(mInitResult
));
281 // Even though this isn't great, we should try to proceed even when
282 // CoInitializeSecurity has previously been called: the additional settings
283 // we want to change are important enough that we don't want to skip them.
284 if (FAILED(mInitResult
) && mInitResult
!= RPC_E_TOO_LATE
) {
288 lock
.SetInitState(ProcessInitState::PartialSecurityInitialized
);
291 if (prevInitState
< ProcessInitState::PartialGlobalOptions
) {
292 RefPtr
<IGlobalOptions
> globalOpts
;
293 mInitResult
= wrapped::CoCreateInstance(
294 CLSID_GlobalOptions
, nullptr, CLSCTX_INPROC_SERVER
, IID_IGlobalOptions
,
295 getter_AddRefs(globalOpts
));
296 MOZ_ASSERT(SUCCEEDED(mInitResult
));
297 if (FAILED(mInitResult
)) {
301 // Disable COM's catch-all exception handler
302 mInitResult
= globalOpts
->Set(COMGLB_EXCEPTION_HANDLING
,
303 COMGLB_EXCEPTION_DONOT_HANDLE_ANY
);
304 MOZ_ASSERT(SUCCEEDED(mInitResult
));
305 if (FAILED(mInitResult
)) {
309 lock
.SetInitState(ProcessInitState::PartialGlobalOptions
);
312 // Disable the BSTR cache (as it never invalidates, thus leaking memory)
313 // (This function is itself idempotent, so we do not concern ourselves with
314 // tracking whether or not we've already called it.)
317 lock
.SetInitState(ProcessInitState::FullyInitialized
);
320 #if defined(MOZILLA_INTERNAL_API)
322 * Guaranteed to run *after* the COM (and possible sandboxing) initialization
323 * has successfully completed and stabilized. This method MUST BE IDEMPOTENT!
325 /* static */ void ProcessRuntime::PostInit() {
326 // Currently "roughed-in" but unused.
328 #endif // defined(MOZILLA_INTERNAL_API)
332 ProcessRuntime::GetClientThreadId() {
334 HRESULT hr
= ::CoGetCallerTID(&callerTid
);
335 // Don't return callerTid unless the call succeeded and returned S_FALSE,
336 // indicating that the caller originates from a different process.
346 ProcessRuntime::InitializeSecurity(const ProcessCategory aProcessCategory
) {
347 HANDLE rawToken
= nullptr;
348 BOOL ok
= ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY
, &rawToken
);
350 return HRESULT_FROM_WIN32(::GetLastError());
352 nsAutoHandle
token(rawToken
);
355 ok
= ::GetTokenInformation(token
, TokenUser
, nullptr, len
, &len
);
356 DWORD win32Error
= ::GetLastError();
357 if (!ok
&& win32Error
!= ERROR_INSUFFICIENT_BUFFER
) {
358 return HRESULT_FROM_WIN32(win32Error
);
361 auto tokenUserBuf
= MakeUnique
<BYTE
[]>(len
);
362 TOKEN_USER
& tokenUser
= *reinterpret_cast<TOKEN_USER
*>(tokenUserBuf
.get());
363 ok
= ::GetTokenInformation(token
, TokenUser
, tokenUserBuf
.get(), len
, &len
);
365 return HRESULT_FROM_WIN32(::GetLastError());
369 ok
= ::GetTokenInformation(token
, TokenPrimaryGroup
, nullptr, len
, &len
);
370 win32Error
= ::GetLastError();
371 if (!ok
&& win32Error
!= ERROR_INSUFFICIENT_BUFFER
) {
372 return HRESULT_FROM_WIN32(win32Error
);
375 auto tokenPrimaryGroupBuf
= MakeUnique
<BYTE
[]>(len
);
376 TOKEN_PRIMARY_GROUP
& tokenPrimaryGroup
=
377 *reinterpret_cast<TOKEN_PRIMARY_GROUP
*>(tokenPrimaryGroupBuf
.get());
378 ok
= ::GetTokenInformation(token
, TokenPrimaryGroup
,
379 tokenPrimaryGroupBuf
.get(), len
, &len
);
381 return HRESULT_FROM_WIN32(::GetLastError());
384 SECURITY_DESCRIPTOR sd
;
385 if (!::InitializeSecurityDescriptor(&sd
, SECURITY_DESCRIPTOR_REVISION
)) {
386 return HRESULT_FROM_WIN32(::GetLastError());
389 BYTE systemSid
[SECURITY_MAX_SID_SIZE
];
390 DWORD systemSidSize
= sizeof(systemSid
);
391 if (!::CreateWellKnownSid(WinLocalSystemSid
, nullptr, systemSid
,
393 return HRESULT_FROM_WIN32(::GetLastError());
396 BYTE adminSid
[SECURITY_MAX_SID_SIZE
];
397 DWORD adminSidSize
= sizeof(adminSid
);
398 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid
, nullptr, adminSid
,
400 return HRESULT_FROM_WIN32(::GetLastError());
403 const bool allowAppContainers
=
404 aProcessCategory
== ProcessCategory::GeckoBrowserParent
&&
407 BYTE appContainersSid
[SECURITY_MAX_SID_SIZE
];
408 DWORD appContainersSidSize
= sizeof(appContainersSid
);
409 if (allowAppContainers
) {
410 if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid
, nullptr,
411 appContainersSid
, &appContainersSidSize
)) {
412 return HRESULT_FROM_WIN32(::GetLastError());
416 // Grant access to SYSTEM, Administrators, the user, and when running as the
417 // browser process on Windows 8+, all app containers.
418 const size_t kMaxInlineEntries
= 4;
419 mozilla::Vector
<EXPLICIT_ACCESS_W
, kMaxInlineEntries
> entries
;
421 Unused
<< entries
.append(EXPLICIT_ACCESS_W
{
425 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
, TRUSTEE_IS_USER
,
426 reinterpret_cast<LPWSTR
>(systemSid
)}});
428 Unused
<< entries
.append(EXPLICIT_ACCESS_W
{
432 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
,
433 TRUSTEE_IS_WELL_KNOWN_GROUP
, reinterpret_cast<LPWSTR
>(adminSid
)}});
435 Unused
<< entries
.append(EXPLICIT_ACCESS_W
{
439 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
, TRUSTEE_IS_USER
,
440 reinterpret_cast<LPWSTR
>(tokenUser
.User
.Sid
)}});
442 if (allowAppContainers
) {
443 Unused
<< entries
.append(
444 EXPLICIT_ACCESS_W
{COM_RIGHTS_EXECUTE
,
447 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
,
448 TRUSTEE_IS_WELL_KNOWN_GROUP
,
449 reinterpret_cast<LPWSTR
>(appContainersSid
)}});
452 PACL rawDacl
= nullptr;
454 ::SetEntriesInAclW(entries
.length(), entries
.begin(), nullptr, &rawDacl
);
455 if (win32Error
!= ERROR_SUCCESS
) {
456 return HRESULT_FROM_WIN32(win32Error
);
459 UniquePtr
<ACL
, LocalFreeDeleter
> dacl(rawDacl
);
461 if (!::SetSecurityDescriptorDacl(&sd
, TRUE
, dacl
.get(), FALSE
)) {
462 return HRESULT_FROM_WIN32(::GetLastError());
465 if (!::SetSecurityDescriptorOwner(&sd
, tokenUser
.User
.Sid
, FALSE
)) {
466 return HRESULT_FROM_WIN32(::GetLastError());
469 if (!::SetSecurityDescriptorGroup(&sd
, tokenPrimaryGroup
.PrimaryGroup
,
471 return HRESULT_FROM_WIN32(::GetLastError());
474 return wrapped::CoInitializeSecurity(
475 &sd
, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT
,
476 RPC_C_IMP_LEVEL_IDENTIFY
, nullptr, EOAC_NONE
, nullptr);
480 } // namespace mozilla