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) && defined(MOZILLA_INTERNAL_API)
10 # include "mozilla/a11y/Compatibility.h"
11 #endif // defined(ACCESSIBILITY) && defined(MOZILLA_INTERNAL_API)
12 #include "mozilla/Assertions.h"
13 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
14 #include "mozilla/mscom/ProcessRuntimeShared.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/UniquePtr.h"
17 #include "mozilla/Unused.h"
18 #include "mozilla/Vector.h"
19 #include "mozilla/WindowsVersion.h"
20 #include "nsWindowsHelpers.h"
22 #if defined(MOZILLA_INTERNAL_API) && defined(MOZ_SANDBOX)
23 # include "mozilla/mscom/EnsureMTA.h"
24 # include "mozilla/sandboxTarget.h"
25 # include "nsThreadManager.h"
26 #endif // defined(MOZILLA_INTERNAL_API) && defined(MOZ_SANDBOX)
33 // This API from oleaut32.dll is not declared in Windows SDK headers
34 extern "C" void __cdecl
SetOaNoCache(void);
39 ProcessRuntime::ProcessRuntime(GeckoProcessType aProcessType
)
40 : ProcessRuntime(aProcessType
== GeckoProcessType_Default
41 ? ProcessCategory::GeckoBrowserParent
42 : ProcessCategory::GeckoChild
) {}
44 ProcessRuntime::ProcessRuntime(ProcessRuntime::ProcessCategory aProcessCategory
)
45 : mInitResult(CO_E_NOTINITIALIZED
),
46 mProcessCategory(aProcessCategory
)
47 #if defined(ACCESSIBILITY) && defined(MOZILLA_INTERNAL_API)
49 mActCtxRgn(a11y::Compatibility::GetActCtxResourceId())
50 #endif // defined(ACCESSIBILITY) && defined(MOZILLA_INTERNAL_API)
52 #if defined(MOZILLA_INTERNAL_API) && defined(MOZ_SANDBOX)
53 // If our process is running under Win32k lockdown, we cannot initialize
54 // COM with single-threaded apartments. This is because STAs create a hidden
55 // window, which implicitly requires user32 and Win32k, which are blocked.
56 // Instead we start a multi-threaded apartment and conduct our process-wide
57 // COM initialization on that MTA background thread.
58 if (mProcessCategory
== ProcessCategory::GeckoChild
&& IsWin32kLockedDown()) {
59 // It is possible that we're running so early that we might need to start
60 // the thread manager ourselves.
61 nsresult rv
= nsThreadManager::get().Init();
62 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
67 // Use the current thread's impersonation token to initialize COM, as
68 // it might fail otherwise (depending on sandbox policy).
69 HANDLE rawCurThreadImpToken
;
70 if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_DUPLICATE
| TOKEN_QUERY
,
71 FALSE
, &rawCurThreadImpToken
)) {
72 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
75 nsAutoHandle
curThreadImpToken(rawCurThreadImpToken
);
78 // Ensure that our current token is still an impersonation token (ie, we
79 // have not yet called RevertToSelf() on this thread).
82 MOZ_ASSERT(::GetTokenInformation(rawCurThreadImpToken
, TokenType
,
83 &tokenType
, sizeof(tokenType
), &len
) &&
84 len
== sizeof(tokenType
) && tokenType
== TokenImpersonation
);
85 # endif // defined(DEBUG)
87 // Create an impersonation token based on the current thread's token
88 HANDLE rawMtaThreadImpToken
= nullptr;
89 if (!::DuplicateToken(rawCurThreadImpToken
, SecurityImpersonation
,
90 &rawMtaThreadImpToken
)) {
91 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
94 nsAutoHandle
mtaThreadImpToken(rawMtaThreadImpToken
);
96 SandboxTarget::Instance()->RegisterSandboxStartCallback([]() -> void {
99 // This is a security risk if it fails, so we release assert
100 MOZ_RELEASE_ASSERT(::RevertToSelf(),
101 "mscom::ProcessRuntime RevertToSelf failed");
103 EnsureMTA::Option::ForceDispatch
);
106 // Impersonate and initialize.
108 [this, rawMtaThreadImpToken
]() -> void {
109 if (!::SetThreadToken(nullptr, rawMtaThreadImpToken
)) {
110 mInitResult
= HRESULT_FROM_WIN32(::GetLastError());
114 InitInsideApartment();
116 EnsureMTA::Option::ForceDispatch
);
121 #endif // defined(MOZILLA_INTERNAL_API)
123 mAptRegion
.Init(GetDesiredApartmentType(mProcessCategory
));
125 // We must be the outermost COM initialization on this thread. The COM runtime
126 // cannot be configured once we start manipulating objects
127 MOZ_ASSERT(mAptRegion
.IsValidOutermost());
128 if (!mAptRegion
.IsValidOutermost()) {
129 mInitResult
= mAptRegion
.GetHResult();
133 InitInsideApartment();
137 COINIT
ProcessRuntime::GetDesiredApartmentType(
138 ProcessRuntime::ProcessCategory aProcessCategory
) {
139 // Gecko processes get single-threaded apartments, others get multithreaded
140 // apartments. We should revisit the GeckoChild case as soon as we deploy
142 switch (aProcessCategory
) {
143 case ProcessCategory::GeckoBrowserParent
:
144 case ProcessCategory::GeckoChild
:
145 return COINIT_APARTMENTTHREADED
;
147 return COINIT_MULTITHREADED
;
151 void ProcessRuntime::InitInsideApartment() {
152 ProcessInitLock lock
;
153 if (lock
.IsInitialized()) {
154 // COM has already been initialized by a previous ProcessRuntime instance
159 // We are required to initialize security prior to configuring global options.
160 mInitResult
= InitializeSecurity();
161 MOZ_ASSERT(SUCCEEDED(mInitResult
));
162 if (FAILED(mInitResult
)) {
166 RefPtr
<IGlobalOptions
> globalOpts
;
167 mInitResult
= ::CoCreateInstance(CLSID_GlobalOptions
, nullptr,
168 CLSCTX_INPROC_SERVER
, IID_IGlobalOptions
,
169 (void**)getter_AddRefs(globalOpts
));
170 MOZ_ASSERT(SUCCEEDED(mInitResult
));
171 if (FAILED(mInitResult
)) {
175 // Disable COM's catch-all exception handler
176 mInitResult
= globalOpts
->Set(COMGLB_EXCEPTION_HANDLING
,
177 COMGLB_EXCEPTION_DONOT_HANDLE_ANY
);
178 MOZ_ASSERT(SUCCEEDED(mInitResult
));
180 // Disable the BSTR cache (as it never invalidates, thus leaking memory)
183 if (FAILED(mInitResult
)) {
187 lock
.SetInitialized();
192 ProcessRuntime::GetClientThreadId() {
194 HRESULT hr
= ::CoGetCallerTID(&callerTid
);
195 // Don't return callerTid unless the call succeeded and returned S_FALSE,
196 // indicating that the caller originates from a different process.
205 ProcessRuntime::InitializeSecurity() {
206 HANDLE rawToken
= nullptr;
207 BOOL ok
= ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY
, &rawToken
);
209 return HRESULT_FROM_WIN32(::GetLastError());
211 nsAutoHandle
token(rawToken
);
214 ok
= ::GetTokenInformation(token
, TokenUser
, nullptr, len
, &len
);
215 DWORD win32Error
= ::GetLastError();
216 if (!ok
&& win32Error
!= ERROR_INSUFFICIENT_BUFFER
) {
217 return HRESULT_FROM_WIN32(win32Error
);
220 auto tokenUserBuf
= MakeUnique
<BYTE
[]>(len
);
221 TOKEN_USER
& tokenUser
= *reinterpret_cast<TOKEN_USER
*>(tokenUserBuf
.get());
222 ok
= ::GetTokenInformation(token
, TokenUser
, tokenUserBuf
.get(), len
, &len
);
224 return HRESULT_FROM_WIN32(::GetLastError());
228 ok
= ::GetTokenInformation(token
, TokenPrimaryGroup
, nullptr, len
, &len
);
229 win32Error
= ::GetLastError();
230 if (!ok
&& win32Error
!= ERROR_INSUFFICIENT_BUFFER
) {
231 return HRESULT_FROM_WIN32(win32Error
);
234 auto tokenPrimaryGroupBuf
= MakeUnique
<BYTE
[]>(len
);
235 TOKEN_PRIMARY_GROUP
& tokenPrimaryGroup
=
236 *reinterpret_cast<TOKEN_PRIMARY_GROUP
*>(tokenPrimaryGroupBuf
.get());
237 ok
= ::GetTokenInformation(token
, TokenPrimaryGroup
,
238 tokenPrimaryGroupBuf
.get(), len
, &len
);
240 return HRESULT_FROM_WIN32(::GetLastError());
243 SECURITY_DESCRIPTOR sd
;
244 if (!::InitializeSecurityDescriptor(&sd
, SECURITY_DESCRIPTOR_REVISION
)) {
245 return HRESULT_FROM_WIN32(::GetLastError());
248 BYTE systemSid
[SECURITY_MAX_SID_SIZE
];
249 DWORD systemSidSize
= sizeof(systemSid
);
250 if (!::CreateWellKnownSid(WinLocalSystemSid
, nullptr, systemSid
,
252 return HRESULT_FROM_WIN32(::GetLastError());
255 BYTE adminSid
[SECURITY_MAX_SID_SIZE
];
256 DWORD adminSidSize
= sizeof(adminSid
);
257 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid
, nullptr, adminSid
,
259 return HRESULT_FROM_WIN32(::GetLastError());
262 BYTE appContainersSid
[SECURITY_MAX_SID_SIZE
];
263 DWORD appContainersSidSize
= sizeof(appContainersSid
);
264 if (mProcessCategory
== ProcessCategory::GeckoBrowserParent
&&
266 if (!::CreateWellKnownSid(WinBuiltinAnyPackageSid
, nullptr,
267 appContainersSid
, &appContainersSidSize
)) {
268 return HRESULT_FROM_WIN32(::GetLastError());
272 // Grant access to SYSTEM, Administrators, the user, and when running as the
273 // browser process on Windows 8+, all app containers.
274 const size_t kMaxInlineEntries
= 4;
275 mozilla::Vector
<EXPLICIT_ACCESS_W
, kMaxInlineEntries
> entries
;
277 Unused
<< entries
.append(EXPLICIT_ACCESS_W
{
281 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
, TRUSTEE_IS_USER
,
282 reinterpret_cast<LPWSTR
>(systemSid
)}});
284 Unused
<< entries
.append(EXPLICIT_ACCESS_W
{
288 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
,
289 TRUSTEE_IS_WELL_KNOWN_GROUP
, reinterpret_cast<LPWSTR
>(adminSid
)}});
291 Unused
<< entries
.append(EXPLICIT_ACCESS_W
{
295 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
, TRUSTEE_IS_USER
,
296 reinterpret_cast<LPWSTR
>(tokenUser
.User
.Sid
)}});
298 if (mProcessCategory
== ProcessCategory::GeckoBrowserParent
&&
300 Unused
<< entries
.append(
301 EXPLICIT_ACCESS_W
{COM_RIGHTS_EXECUTE
,
304 {nullptr, NO_MULTIPLE_TRUSTEE
, TRUSTEE_IS_SID
,
305 TRUSTEE_IS_WELL_KNOWN_GROUP
,
306 reinterpret_cast<LPWSTR
>(appContainersSid
)}});
309 PACL rawDacl
= nullptr;
311 ::SetEntriesInAclW(entries
.length(), entries
.begin(), nullptr, &rawDacl
);
312 if (win32Error
!= ERROR_SUCCESS
) {
313 return HRESULT_FROM_WIN32(win32Error
);
316 UniquePtr
<ACL
, LocalFreeDeleter
> dacl(rawDacl
);
318 if (!::SetSecurityDescriptorDacl(&sd
, TRUE
, dacl
.get(), FALSE
)) {
319 return HRESULT_FROM_WIN32(::GetLastError());
322 if (!::SetSecurityDescriptorOwner(&sd
, tokenUser
.User
.Sid
, FALSE
)) {
323 return HRESULT_FROM_WIN32(::GetLastError());
326 if (!::SetSecurityDescriptorGroup(&sd
, tokenPrimaryGroup
.PrimaryGroup
,
328 return HRESULT_FROM_WIN32(::GetLastError());
331 return ::CoInitializeSecurity(
332 &sd
, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT
,
333 RPC_C_IMP_LEVEL_IDENTIFY
, nullptr, EOAC_NONE
, nullptr);
337 } // namespace mozilla