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)
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
;
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
);
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
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
85 HANDLE rawCurThreadImpToken
;
86 if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_DUPLICATE
| TOKEN_QUERY
,
87 FALSE
, &rawCurThreadImpToken
)) {
88 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
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).
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
106 if (!isCurThreadImplicitMTA
) {
107 InitUsingPersistentMTAThread(curThreadImpToken
);
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
129 ProcessInitLock lock
;
131 // Is another instance of ProcessRuntime responsible for the outer
133 const bool prevInit
=
134 lock
.GetInitState() == ProcessInitState::FullyInitialized
;
135 MOZ_ASSERT(prevInit
);
139 #endif // defined(MOZILLA_INTERNAL_API)
143 InitInsideApartment();
144 if (FAILED(mInitResult
)) {
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
)) {
161 # endif // defined(MOZ_SANDBOX)
164 #endif // defined(MOZILLA_INTERNAL_API)
167 #if defined(MOZILLA_INTERNAL_API)
168 ProcessRuntime::~ProcessRuntime() {
169 MOZ_DIAGNOSTIC_ASSERT(sInstance
== this);
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());
183 nsAutoHandle
mtaThreadImpToken(rawMtaThreadImpToken
);
185 // Impersonate and initialize.
186 bool tokenSet
= false;
188 [this, rawMtaThreadImpToken
, &tokenSet
]() -> void {
189 if (!::SetThreadToken(nullptr, rawMtaThreadImpToken
)) {
190 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
195 InitInsideApartment();
197 EnsureMTA::Option::ForceDispatchToPersistentThread
);
203 SandboxTarget::Instance()->RegisterSandboxStartCallback(
204 [self
= this]() -> 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
)) {
220 # endif // defined(MOZ_SANDBOX)
221 #endif // defined(MOZILLA_INTERNAL_API)
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
);
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
252 if (prevInitState
< ProcessInitState::PartialSecurityInitialized
) {
253 // We are required to initialize security prior to configuring global
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
) {
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
)) {
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
)) {
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.)
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)
309 ProcessRuntime::GetClientThreadId() {
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.
323 ProcessRuntime::InitializeSecurity(const ProcessCategory aProcessCategory
) {
324 HANDLE rawToken
= nullptr;
325 BOOL ok
= ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY
, &rawToken
);
327 return HRESULT_FROM_WIN32(::GetLastError());
329 nsAutoHandle
token(rawToken
);
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
);
342 return HRESULT_FROM_WIN32(::GetLastError());
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
);
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
,
370 return HRESULT_FROM_WIN32(::GetLastError());
373 BYTE adminSid
[SECURITY_MAX_SID_SIZE
];
374 DWORD adminSidSize
= sizeof(adminSid
);
375 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid
, nullptr, adminSid
,
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
;
394 ::GetTokenInformation(token
, TokenAppContainerSid
, nullptr, len
, &len
);
396 tokenAppContainerInfBuf
= MakeUnique
<BYTE
[]>(len
);
397 ok
= ::GetTokenInformation(token
, TokenAppContainerSid
,
398 tokenAppContainerInfBuf
.get(), len
, &len
);
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
{
415 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
, TRUSTEE_IS_USER
,
416 reinterpret_cast<LPWSTR
>(systemSid
)}});
418 Unused
<< entries
.append(EXPLICIT_ACCESS_W
{
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
{
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
,
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
{
453 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
, TRUSTEE_IS_USER
,
454 reinterpret_cast<LPWSTR
>(tokenAppContainerInf
.TokenAppContainer
)}});
458 PACL rawDacl
= nullptr;
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
,
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);
486 } // namespace mozilla